mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-07-01 05:46:06 -04:00
--User changes
-Add a palette editor. -Add support for reading .ugr/.gradient/.gradients palette files. -Allow toggling on spinners whose minimum value is not zero. -Allow toggling display of image, affines and grid. -Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial. --Bug fixes -cpow2 was wrong. -Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3. -Use exec() on Apple and show() on all other OSes for dialog boxes. -Trying to render a sequence with no frames would crash. -Selecting multiple xforms and rotating them would produce the wrong rotation. -Better handling when parsing flames using different encoding, such as unicode and UTF-8. -Switching between SP/DP didn't reselect the selected flame in the Library tab. --Code changes -Make all types concerning palettes be floats, including PaletteTableWidgetItem. -PaletteTableWidgetItem is no longer templated because all palettes are float. -Include the source colors for user created gradients. -Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems. -Split conditional out of accumulation loop on the CPU for better performance. -Vectorize summing when doing density filter for better performance. -Make all usage of palettes be of type float, double is pointless. -Allow palettes to reside in multiple folders, while ensuring only one of each name is added. -Refactor some palette path searching code. -Make ReadFile() throw and catch an exception if the file operation fails. -A little extra safety in foci and foci3D with a call to Zeps(). -Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac. -Fixing missing comma between paths in InitPaletteList(). -Move Xml and PaletteList classes into cpp to shorten build times when working on them. -Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file. -Change more NULL to nullptr.
This commit is contained in:
@ -58,7 +58,7 @@
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><br/>Fractorium 1.0.0.2</p><p align="center"><span style=" font-size:10pt;">A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><a href="http://fractorium.com">fractorium.com</a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge</span></p></body></html></string>
|
||||
<string><html><head/><body><p align="center">Fractorium 1.0.0.2</p><p align="center"><span style=" font-size:10pt;">A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><a href="http://fractorium.com"><span style=" text-decoration: underline; color:#0000ff;">fractorium.com</span></a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge, Michel Mastriani</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
@ -86,9 +86,9 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>5</x>
|
||||
<y>180</y>
|
||||
<y>170</y>
|
||||
<width>585</width>
|
||||
<height>483</height>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="AboutVerticalLayout">
|
||||
@ -128,7 +128,7 @@
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="https://github.com/scottdraves/flam3"><span style=" text-decoration: underline; color:#0000ff;">flam3</span></a>: Scott Draves, Erik Reckase (GPL v2)<br/><a href="http://github.com/stevenrobertson/cuburn"><span style=" text-decoration: underline; color:#0000ff;">cuburn</span></a>: Steven Robertson, Michael Semeniuk, Matthew Znoj, Nicolas Mejia (GPL v3)<br/><a href="http://fractron9000.sourceforge.net"><span style=" text-decoration: underline; color:#0000ff;">Fractron 9000</span></a>: Mike Thiesen (GPL)<br/><a href="http://sourceforge.net/projects/apophysis7x"><span style=" text-decoration: underline; color:#0000ff;">Apophysis</span></a>: Mark Townsend, Ronald Hordijk, Peter Sdobnov, Piotr Borys, Georg Kiehne (GPL)<br/><a href="http://jwildfire.org/"><span style=" text-decoration: underline; color:#0000ff;">JWildfire</span></a>: Andreas Maschke (LGPL)<br/>Numerous Apophysis plugin developers (GPL)</p></body></html></string>
|
||||
<string><html><head/><body><p><a href="https://github.com/scottdraves/flam3"><span style=" text-decoration: underline; color:#0000ff;">flam3</span></a>: Scott Draves, Erik Reckase (GPL v2)<br/><a href="http://github.com/stevenrobertson/cuburn"><span style=" text-decoration: underline; color:#0000ff;">cuburn</span></a>: Steven Robertson, Michael Semeniuk, Matthew Znoj, Nicolas Mejia (GPL v3)<br/><a href="http://fractron9000.sourceforge.net"><span style=" text-decoration: underline; color:#0000ff;">Fractron 9000</span></a>: Mike Thiesen (GPL)<br/><a href="http://sourceforge.net/projects/apophysis7x"><span style=" text-decoration: underline; color:#0000ff;">Apophysis</span></a>: Mark Townsend, Ronald Hordijk, Peter Sdobnov, Piotr Borys, Georg Kiehne (GPL)<br/><a href="http://jwildfire.org/"><span style=" text-decoration: underline; color:#0000ff;">JWildfire</span></a>: Andreas Maschke (LGPL)<br/>gradLib: Stian Broen<br/>ColorPickerWidget: Etienne Moutot<br/>Numerous Apophysis plugin developers (GPL)</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
@ -211,7 +211,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Icons Used</string>
|
||||
<string>Icons and Palettes Used</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="topMargin">
|
||||
@ -232,7 +232,7 @@
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://famfamfam.com"><span style=" text-decoration: underline; color:#0000ff;">Silk</span></a>: Mark James (Creative Commons Attribution 2.5 License)<br/><a href="http://momentumdesignlab.com"><span style=" text-decoration: underline; color:#0000ff;">Momentum</span></a>: Momentum Design Lab (Creative Commons Attribution-ShareAlike 3.5 License)<br/><a href="http://everaldo.com"><span style=" text-decoration: underline; color:#0000ff;">Crystal Clear</span></a>: Everaldo Coelho (LGPL)<br/><a href="http://openiconlibrary.sourceforge.net"><span style=" text-decoration: underline; color:#0000ff;">Open Icon Library</span></a>: Jeff Israel (GPL, LGPL, Creative Commons, Public Domain)<br/><a href="http://icons.mysitemyway.com/category/3d-transparent-glass-icons/"><span style=" text-decoration: underline; color:#0000ff;">3D Transparent Glass</span></a>: iconsETC (Public Domain)<br/><a href="http://p.yusukekamiyamane.com"><span style=" text-decoration: underline; color:#0000ff;">Fugue</span></a>: Yusuke Kamiyamane (Creative Commons Attribution 3.0 License)</p></body></html></string>
|
||||
<string><html><head/><body><p><span style=" text-decoration: underline; color:#0000ff;">Palettes</span>: Boxtail, FarDareisMai, FractalDesire, Rce, Tatasz (Public Domain)<br/><a href="http://famfamfam.com"><span style=" text-decoration: underline; color:#0000ff;">Silk</span></a>: Mark James (Creative Commons Attribution 2.5 License)<br/><a href="http://momentumdesignlab.com"><span style=" text-decoration: underline; color:#0000ff;">Momentum</span></a>: Momentum Design Lab (Creative Commons Attribution-ShareAlike 3.5 License)<br/><a href="http://everaldo.com"><span style=" text-decoration: underline; color:#0000ff;">Crystal Clear</span></a>: Everaldo Coelho (LGPL)<br/><a href="http://openiconlibrary.sourceforge.net"><span style=" text-decoration: underline; color:#0000ff;">Open Icon Library</span></a>: Jeff Israel (GPL, LGPL, Creative Commons, Public Domain)<br/><a href="http://icons.mysitemyway.com/category/3d-transparent-glass-icons/"><span style=" text-decoration: underline; color:#0000ff;">3D Transparent Glass</span></a>: iconsETC (Public Domain)<br/><a href="http://p.yusukekamiyamane.com"><span style=" text-decoration: underline; color:#0000ff;">Fugue</span></a>: Yusuke Kamiyamane (Creative Commons Attribution 3.0 License)</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
@ -253,7 +253,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="">
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>5</x>
|
||||
|
@ -32,7 +32,7 @@ class CurvesGraphicsView : public QGraphicsView
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CurvesGraphicsView(QWidget* parent = 0);
|
||||
CurvesGraphicsView(QWidget* parent = nullptr);
|
||||
|
||||
void PointChanged(int curveIndex, int pointIndex, const QPointF& point);
|
||||
QPointF Get(int curveIndex, int pointIndex);
|
||||
@ -82,7 +82,7 @@ public:
|
||||
/// <param name="pointIndex">The point index within the curve</param>
|
||||
/// <param name="viewParent">The graphics view this point is displayed on</param>
|
||||
/// <param name="p">The parent widget of this item</param>
|
||||
EllipseItem(const QRectF& rect, int curveIndex, int pointIndex, CurvesGraphicsView* viewParent, QGraphicsItem* parent = 0)
|
||||
EllipseItem(const QRectF& rect, int curveIndex, int pointIndex, CurvesGraphicsView* viewParent, QGraphicsItem* parent = nullptr)
|
||||
: QGraphicsEllipseItem(rect, parent)
|
||||
{
|
||||
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "DoubleSpinBox.h"
|
||||
#include "FractoriumSettings.h"
|
||||
|
||||
QTimer DoubleSpinBox::s_Timer;
|
||||
|
||||
@ -17,10 +16,12 @@ DoubleSpinBox::DoubleSpinBox(QWidget* p, int h, double step)
|
||||
: QDoubleSpinBox(p)
|
||||
{
|
||||
m_DoubleClick = false;
|
||||
m_DoubleClickLowVal = 0;
|
||||
m_DoubleClickNonZero = 0;
|
||||
m_DoubleClickZero = 1;
|
||||
m_Step = step;
|
||||
m_SmallStep = step / 10.0;
|
||||
m_Settings = FractoriumSettings::DefInstance();
|
||||
setSingleStep(step);
|
||||
setFrame(false);
|
||||
setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
@ -53,6 +54,16 @@ void DoubleSpinBox::DoubleClick(bool b)
|
||||
m_DoubleClick = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used instead of zero to represent the lower value
|
||||
/// used when responding to a double click.
|
||||
/// </summary>
|
||||
/// <param name="val">The value to be used for the lower value instead of zero</param>
|
||||
void DoubleSpinBox::DoubleClickLowVal(double val)
|
||||
{
|
||||
m_DoubleClickLowVal = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used when the user double clicks the spinner while
|
||||
/// it contains zero.
|
||||
@ -120,7 +131,7 @@ QLineEdit* DoubleSpinBox::lineEdit()
|
||||
/// <summary>
|
||||
/// Another workaround for the persistent text selection bug in Qt.
|
||||
/// </summary>
|
||||
void DoubleSpinBox::OnSpinBoxValueChanged(double d)
|
||||
void DoubleSpinBox::OnSpinBoxValueChanged(double)
|
||||
{
|
||||
lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected.
|
||||
}
|
||||
@ -159,35 +170,34 @@ void DoubleSpinBox::OnTimeout()
|
||||
bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
|
||||
{
|
||||
QMouseEvent* me = dynamic_cast<QMouseEvent*>(e);
|
||||
auto settings = FractoriumSettings::DefInstance();
|
||||
|
||||
if (isEnabled() && me)
|
||||
{
|
||||
if (!settings->ToggleType() &&//Ensure double click toggles, not right click.
|
||||
if (!m_Settings->ToggleType() &&//Ensure double click toggles, not right click.
|
||||
me->type() == QMouseEvent::MouseButtonPress &&
|
||||
me->button() == Qt::RightButton)
|
||||
{
|
||||
m_MouseDownPoint = m_MouseMovePoint = me->pos();
|
||||
StartTimer();
|
||||
}
|
||||
else if (!settings->ToggleType() &&
|
||||
else if (!m_Settings->ToggleType() &&
|
||||
me->type() == QMouseEvent::MouseButtonRelease &&
|
||||
me->button() == Qt::RightButton)
|
||||
{
|
||||
StopTimer();
|
||||
m_MouseDownPoint = m_MouseMovePoint = me->pos();
|
||||
}
|
||||
else if (!settings->ToggleType() &&
|
||||
else if (!m_Settings->ToggleType() &&
|
||||
me->type() == QMouseEvent::MouseMove &&
|
||||
QGuiApplication::mouseButtons() & Qt::RightButton)
|
||||
{
|
||||
m_MouseMovePoint = me->pos();
|
||||
}
|
||||
else if (m_DoubleClick &&
|
||||
((!settings->ToggleType() && e->type() == QMouseEvent::MouseButtonDblClick && me->button() == Qt::LeftButton) ||
|
||||
(settings->ToggleType() && me->type() == QMouseEvent::MouseButtonRelease && me->button() == Qt::RightButton)))
|
||||
((!m_Settings->ToggleType() && e->type() == QMouseEvent::MouseButtonDblClick && me->button() == Qt::LeftButton) ||
|
||||
(m_Settings->ToggleType() && me->type() == QMouseEvent::MouseButtonRelease && me->button() == Qt::RightButton)))
|
||||
{
|
||||
if (IsNearZero(value()))
|
||||
if (IsClose(m_DoubleClickLowVal, value()))
|
||||
setValue(m_DoubleClickZero);
|
||||
else
|
||||
setValue(m_DoubleClickNonZero);
|
||||
@ -276,4 +286,4 @@ void DoubleSpinBox::StopTimer()
|
||||
{
|
||||
s_Timer.stop();
|
||||
disconnect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "FractoriumSettings.h"
|
||||
|
||||
/// <summary>
|
||||
/// DoubleSpinBox and VariationTreeDoubleSpinBox classes.
|
||||
@ -22,6 +23,7 @@ public:
|
||||
virtual ~DoubleSpinBox() { }
|
||||
void SetValueStealth(double d);
|
||||
void DoubleClick(bool b);
|
||||
void DoubleClickLowVal(double val);
|
||||
void DoubleClickZero(double val);
|
||||
void DoubleClickNonZero(double val);
|
||||
double Step();
|
||||
@ -46,12 +48,14 @@ private:
|
||||
void StopTimer();
|
||||
|
||||
bool m_DoubleClick;
|
||||
double m_DoubleClickLowVal;
|
||||
double m_DoubleClickNonZero;
|
||||
double m_DoubleClickZero;
|
||||
double m_Step;
|
||||
double m_SmallStep;
|
||||
QPoint m_MouseDownPoint;
|
||||
QPoint m_MouseMovePoint;
|
||||
shared_ptr<FractoriumSettings> m_Settings;
|
||||
static QTimer s_Timer;
|
||||
};
|
||||
|
||||
|
@ -31,8 +31,8 @@ public:
|
||||
/// The re-parenting is done so that the DoubleSpinBox appears directly on top of the cell.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent cell</param>
|
||||
/// <param name="option">Unused</param>
|
||||
/// <param name="index">unused</param>
|
||||
/// <param name="option">Ignored</param>
|
||||
/// <param name="index">Ignored</param>
|
||||
/// <returns>The DoubleSpinBox member</returns>
|
||||
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
|
||||
{
|
||||
@ -44,8 +44,8 @@ public:
|
||||
/// <summary>
|
||||
/// Prevent DoubleSpinBox control from being destroyed when the cell loses focus.
|
||||
/// </summary>
|
||||
/// <param name="editor">Unused</param>
|
||||
/// <param name="index">Unused</param>
|
||||
/// <param name="editor">Ignored</param>
|
||||
/// <param name="index">Ignored</param>
|
||||
virtual void destroyEditor(QWidget* editor, const QModelIndex& index) const override
|
||||
{
|
||||
}
|
||||
@ -53,8 +53,8 @@ public:
|
||||
/// <summary>
|
||||
/// Set the value of the DoubleSpinBox as well as its tableindex property.
|
||||
/// </summary>
|
||||
/// <param name="editor">Unused</param>
|
||||
/// <param name="index">Unused</param>
|
||||
/// <param name="editor">Ignored</param>
|
||||
/// <param name="index">Ignored</param>
|
||||
virtual void setEditorData(QWidget* editor, const QModelIndex& index) const override
|
||||
{
|
||||
QPoint p(index.row(), index.column());
|
||||
@ -67,7 +67,7 @@ public:
|
||||
/// <summary>
|
||||
/// Set the cell in the model to the value of the DoubleSpinBox.
|
||||
/// </summary>
|
||||
/// <param name="editor">Unused</param>
|
||||
/// <param name="editor">Ignored</param>
|
||||
/// <param name="model">The model whose value will be set</param>
|
||||
/// <param name="index">The cell index of the model</param>
|
||||
virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override
|
||||
@ -80,7 +80,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="editor">The DoubleSpinBox member</param>
|
||||
/// <param name="option">Contains the rectangle to be used for the geometry of the DoubleSpinBox</param>
|
||||
/// <param name="index">Unused</param>
|
||||
/// <param name="index">Ignored</param>
|
||||
virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override
|
||||
{
|
||||
editor->setGeometry(option.rect);
|
||||
|
@ -196,7 +196,11 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
|
||||
void FractoriumFinalRenderDialog::Show(bool fromSequence)
|
||||
{
|
||||
m_FromSequence = fromSequence;
|
||||
#ifdef __APPLE__
|
||||
exec();
|
||||
#else
|
||||
show();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -186,6 +186,19 @@ Fractorium::Fractorium(QWidget* p)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
for (auto dock : m_Docks)//Fixes focus problem on OSX.
|
||||
{
|
||||
if (!dock->isHidden())
|
||||
{
|
||||
dock->setFloating(!dock->isFloating());
|
||||
dock->setFloating(!dock->isFloating());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
//At this point, everything has been setup except the renderer. Shortly after
|
||||
//this constructor exits, GLWidget::InitGL() will create the initial flock and start the rendering timer
|
||||
//which executes whenever the program is idle. Upon starting the timer, the renderer
|
||||
@ -201,6 +214,8 @@ Fractorium::~Fractorium()
|
||||
{
|
||||
SyncSequenceSettings();
|
||||
m_VarDialog->SyncSettings();
|
||||
m_Settings->ShowXforms(ui.ActionDrawXforms->isChecked());
|
||||
m_Settings->ShowGrid(ui.ActionDrawGrid->isChecked());
|
||||
m_Settings->setValue("windowState", saveState());
|
||||
m_Settings->sync();
|
||||
}
|
||||
@ -497,16 +512,13 @@ QStringList Fractorium::SetupOpenXmlDialog()
|
||||
m_FileDialog->setViewMode(QFileDialog::List);
|
||||
}
|
||||
|
||||
if (!m_FileDialog)
|
||||
return QStringList("");
|
||||
|
||||
QStringList filenames;
|
||||
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
|
||||
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->OpenXmlExt(filter); });
|
||||
m_FileDialog->setFileMode(QFileDialog::ExistingFiles);
|
||||
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
|
||||
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->selectNameFilter(m_Settings->OpenXmlExt());
|
||||
|
||||
@ -709,15 +721,20 @@ void Fractorium::SetTabOrders()
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomizeStaggerCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceStaggerSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomStaggerMaxSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomizeRotationsCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRotationsSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRotationsCWCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomRotationsMaxSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomizeFramesPerRotCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceFramesPerRotSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomFramesPerRotMaxSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomizeRotationsCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRotationsSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomRotationsMaxSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomizeBlendFramesCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceBlendFramesSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomBlendMaxFramesSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRandomizeRotationsPerBlendCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRotationsPerBlendSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRotationsPerBlendCWCheckBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceRotationsPerBlendMaxSpinBox);
|
||||
w = SetTabOrder(this, w, ui.SequenceGenerateButton);
|
||||
w = SetTabOrder(this, w, ui.SequenceRenderButton);
|
||||
w = SetTabOrder(this, w, ui.SequenceSaveButton);
|
||||
@ -738,7 +755,6 @@ void Fractorium::SetTabOrders()
|
||||
w = SetTabOrder(this, w, m_XformOpacitySpin);
|
||||
w = SetTabOrder(this, w, m_XformDirectColorSpin);
|
||||
w = SetTabOrder(this, w, ui.SoloXformCheckBox);
|
||||
w = SetTabOrder(this, ui.LockAffineCheckBox, ui.PreAffineGroupBox);//Xforms affine.
|
||||
w = SetTabOrder(this, w, m_PreX1Spin);
|
||||
w = SetTabOrder(this, w, m_PreX2Spin);
|
||||
w = SetTabOrder(this, w, m_PreY1Spin);
|
||||
@ -802,8 +818,9 @@ void Fractorium::SetTabOrders()
|
||||
w = SetTabOrder(this, w, m_PaletteBlurSpin);
|
||||
w = SetTabOrder(this, w, m_PaletteBrightnessSpin);
|
||||
w = SetTabOrder(this, w, m_PaletteFrequencySpin);
|
||||
w = SetTabOrder(this, w, ui.PaletteRandomSelect);
|
||||
w = SetTabOrder(this, w, ui.PaletteRandomAdjust);
|
||||
w = SetTabOrder(this, w, ui.PaletteRandomSelectButton);
|
||||
w = SetTabOrder(this, w, ui.PaletteRandomAdjustButton);
|
||||
w = SetTabOrder(this, w, ui.PaletteEditorButton);
|
||||
w = SetTabOrder(this, w, ui.PaletteFilterLineEdit);
|
||||
w = SetTabOrder(this, w, ui.PaletteFilterClearButton);
|
||||
w = SetTabOrder(this, w, ui.PaletteListTable);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "AboutDialog.h"
|
||||
#include "CurvesGraphicsView.h"
|
||||
#include "DoubleSpinBoxTableItemDelegate.h"
|
||||
#include "PaletteEditor/PaletteEditor.h"
|
||||
|
||||
/// <summary>
|
||||
/// Fractorium class.
|
||||
@ -90,6 +91,11 @@ public:
|
||||
Fractorium(QWidget* p = nullptr);
|
||||
~Fractorium();
|
||||
|
||||
//Toolbar.
|
||||
bool DrawXforms();
|
||||
bool DrawImage();
|
||||
bool DrawGrid();
|
||||
|
||||
//Library.
|
||||
void SyncFileCountToSequenceCount();
|
||||
|
||||
@ -135,6 +141,8 @@ public slots:
|
||||
void OnActionPasteSelectedXforms(bool checked);
|
||||
|
||||
void OnActionResetWorkspace(bool checked);//View
|
||||
void OnActionAlternateEditorImage(bool checked);
|
||||
void OnActionResetScale(bool checked);
|
||||
|
||||
void OnActionAddReflectiveSymmetry(bool checked);//Tools.
|
||||
void OnActionAddRotationalSymmetry(bool checked);
|
||||
@ -158,6 +166,9 @@ public slots:
|
||||
void OnActionDP(bool checked);
|
||||
void OnActionStyle(bool checked);
|
||||
void OnActionStartStopRenderer(bool checked);
|
||||
void OnActionDrawXforms(bool checked);
|
||||
void OnActionDrawImage(bool checked);
|
||||
void OnActionDrawGrid(bool checked);
|
||||
|
||||
//Library.
|
||||
void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col);
|
||||
@ -237,7 +248,6 @@ public slots:
|
||||
void OnXformAnimateCheckBoxStateChanged(int state);
|
||||
|
||||
//Xforms Affine.
|
||||
void OnLockAffineScaleCheckBoxStateChanged(int state);
|
||||
void OnPreAffineRowDoubleClicked(int logicalIndex);
|
||||
void OnPreAffineColDoubleClicked(int logicalIndex);
|
||||
void OnPostAffineRowDoubleClicked(int logicalIndex);
|
||||
@ -275,6 +285,9 @@ public slots:
|
||||
void OnXformScrollColorIndexChanged(int d);
|
||||
void OnRandomColorIndicesButtonClicked(bool b);
|
||||
void OnToggleColorIndicesButtonClicked(bool b);
|
||||
void OnRandomColorSpeedButtonClicked(bool b);
|
||||
void OnToggleColorSpeedButtonClicked(bool b);
|
||||
|
||||
void OnXformColorSpeedChanged(double d);
|
||||
void OnXformOpacityChanged(double d);
|
||||
void OnXformDirectColorChanged(double d);
|
||||
@ -313,9 +326,12 @@ public slots:
|
||||
void OnPaletteCellDoubleClicked(int row, int col);
|
||||
void OnPaletteRandomSelectButtonClicked(bool checked);
|
||||
void OnPaletteRandomAdjustButtonClicked(bool checked);
|
||||
void OnPaletteEditorButtonClicked(bool checked);
|
||||
void OnPaletteFilterLineEditTextChanged(const QString& text);
|
||||
void OnPaletteFilterClearButtonClicked(bool checked);
|
||||
void OnPaletteHeaderSectionClicked(int col);
|
||||
void OnPaletteEditorColorChanged();
|
||||
void OnPaletteEditorFileChanged();
|
||||
|
||||
//Info.
|
||||
void OnSummaryTableHeaderResized(int logicalIndex, int oldSize, int newSize);
|
||||
@ -371,6 +387,7 @@ private:
|
||||
void SyncOptionsToToolbar();
|
||||
|
||||
//Library.
|
||||
void SelectLibraryItem(size_t index);
|
||||
vector<pair<size_t, QTreeWidgetItem*>> GetCurrentEmberIndex();
|
||||
void SyncSequenceSettings();
|
||||
|
||||
@ -384,7 +401,6 @@ private:
|
||||
|
||||
//Xforms Variations.
|
||||
void Filter();
|
||||
void Filter(const QString& text);
|
||||
|
||||
//Xforms Selection.
|
||||
void ClearXformsSelections();
|
||||
@ -397,6 +413,7 @@ private:
|
||||
void ResetPaletteControls();
|
||||
void SetPaletteFileComboIndex(const string& filename);
|
||||
void SetPaletteTableItem(QPixmap* pixmap, QTableWidget* table, QTableWidgetItem* item, int row, int col);
|
||||
bool PaletteChanged();
|
||||
|
||||
//Info.
|
||||
void FillSummary();
|
||||
@ -415,11 +432,12 @@ private:
|
||||
QString SetupSaveXmlDialog(const QString& defaultFilename);
|
||||
QString SetupSaveImageDialog(const QString& defaultFilename);
|
||||
QString SetupSaveFolderDialog();
|
||||
QColorDialog* m_ColorDialog;
|
||||
FractoriumFinalRenderDialog* m_FinalRenderDialog;
|
||||
FractoriumOptionsDialog* m_OptionsDialog;
|
||||
FractoriumVariationsDialog* m_VarDialog;
|
||||
FractoriumAboutDialog* m_AboutDialog;
|
||||
QColorDialog* m_ColorDialog = nullptr;
|
||||
FractoriumFinalRenderDialog* m_FinalRenderDialog = nullptr;
|
||||
FractoriumOptionsDialog* m_OptionsDialog = nullptr;
|
||||
FractoriumVariationsDialog* m_VarDialog = nullptr;
|
||||
FractoriumAboutDialog* m_AboutDialog = nullptr;
|
||||
PaletteEditor* m_PaletteEditor = nullptr;
|
||||
|
||||
//Params.
|
||||
DoubleSpinBox* m_BrightnessSpin;//Color.
|
||||
@ -494,6 +512,8 @@ private:
|
||||
DoubleSpinBoxTableItemDelegate* m_XaosTableItemDelegate;
|
||||
|
||||
//Palette.
|
||||
bool m_PaletteChanged;
|
||||
bool m_PaletteFileChanged;
|
||||
SpinBox* m_PaletteHueSpin;
|
||||
SpinBox* m_PaletteSaturationSpin;
|
||||
SpinBox* m_PaletteBrightnessSpin;
|
||||
@ -510,9 +530,9 @@ private:
|
||||
QTableWidgetItem* m_InfoFinalXformItem;
|
||||
|
||||
//Files.
|
||||
QFileDialog* m_FileDialog;
|
||||
QFileDialog* m_FolderDialog;
|
||||
QssDialog* m_QssDialog;
|
||||
QFileDialog* m_FileDialog = nullptr;
|
||||
QFileDialog* m_FolderDialog = nullptr;
|
||||
QssDialog* m_QssDialog = nullptr;
|
||||
QString m_LastSaveAll;
|
||||
QString m_LastSaveCurrent;
|
||||
QString m_Style;
|
||||
|
@ -47,5 +47,8 @@
|
||||
<file>Icons/checkbox_unchecked.png</file>
|
||||
<file>Icons/control.png</file>
|
||||
<file>Icons/control-stop-square.png</file>
|
||||
<file>Icons/Function-512.png</file>
|
||||
<file>Icons/pic.png</file>
|
||||
<file>Icons/grid.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -74,7 +74,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1284</width>
|
||||
<width>1269</width>
|
||||
<height>987</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -2007,13 +2007,13 @@
|
||||
<rect>
|
||||
<x>770</x>
|
||||
<y>0</y>
|
||||
<width>255</width>
|
||||
<width>294</width>
|
||||
<height>881</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>255</width>
|
||||
<width>294</width>
|
||||
<height>617</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -2420,7 +2420,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="PaletteRandomSelect">
|
||||
<widget class="QPushButton" name="PaletteRandomSelectButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
@ -2442,7 +2442,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="PaletteRandomAdjust">
|
||||
<widget class="QPushButton" name="PaletteRandomAdjustButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
@ -2466,6 +2466,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="PaletteEditorButton">
|
||||
<property name="toolTip">
|
||||
<string>Open the palette editor to make a custom palette</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Palette Editor...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
@ -3050,7 +3060,7 @@
|
||||
<enum>QTabWidget::Triangular</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="XformColorTab">
|
||||
<property name="sizePolicy">
|
||||
@ -3370,6 +3380,53 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="RandomColorIndicesButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set all xform color indices to random numbers between 0 and 1, inclusive</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Random Indices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="ToggleColorIndicesButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set all xform color indices to 0 or 1</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Toggle Indices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="RandomColorSpeedButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set all xform color indices to 0 or 1</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Random Color Speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="ToggleColorSpeedButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set all xform color indices to 0 or 1</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Toggle Color Speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="TableWidget" name="XformColorValuesTable">
|
||||
<property name="sizePolicy">
|
||||
@ -3565,36 +3622,6 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="RandomColorIndicesButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set all xform color indices to random numbers between 0 and 1, inclusive</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Random Indices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ToggleColorIndicesButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Set all xform color indices to 0 or 1</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Toggle Indices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="SoloXformCheckBox">
|
||||
<property name="toolTip">
|
||||
@ -3642,16 +3669,6 @@
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="LockAffineCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Checking this box will lock the display size of the affines at their current level, allowing for easy manipulation no matter what zoom level is used.</p><p>Unchecking this resets the scale used to draw the affines to its native value.</p><p>Note this only affects the display and does not alter any internal values used for rendering.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lock Affine Scale</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="AffineTabScrollArea">
|
||||
<property name="autoFillBackground">
|
||||
@ -3672,7 +3689,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>263</width>
|
||||
<height>700</height>
|
||||
<height>722</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
@ -6843,6 +6860,8 @@
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<addaction name="ActionResetWorkspace"/>
|
||||
<addaction name="ActionAlternateEditorImage"/>
|
||||
<addaction name="ActionResetScale"/>
|
||||
</widget>
|
||||
<addaction name="MenuFile"/>
|
||||
<addaction name="MenuEdit"/>
|
||||
@ -6997,7 +7016,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>432</width>
|
||||
<height>572</height>
|
||||
<height>589</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -7685,6 +7704,11 @@
|
||||
<addaction name="ActionOptions"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="ActionStyle"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="ActionDrawXforms"/>
|
||||
<addaction name="ActionDrawImage"/>
|
||||
<addaction name="ActionDrawGrid"/>
|
||||
<addaction name="ActionResetScale"/>
|
||||
</widget>
|
||||
<action name="ActionNewFlock">
|
||||
<property name="icon">
|
||||
@ -7870,6 +7894,9 @@
|
||||
<property name="toolTip">
|
||||
<string>Add a copy of the current flame to the end of the current file</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+J</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionClearFlame">
|
||||
<property name="icon">
|
||||
@ -8128,6 +8155,67 @@
|
||||
<string>Ctrl+P</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionDrawImage">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="Fractorium.qrc">
|
||||
<normaloff>:/Fractorium/Icons/pic.png</normaloff>:/Fractorium/Icons/pic.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show/Hide Image</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show/Hide Image</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionDrawGrid">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="Fractorium.qrc">
|
||||
<normaloff>:/Fractorium/Icons/grid.png</normaloff>:/Fractorium/Icons/grid.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show/Hide Grid</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show/Hide Grid</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionDrawXforms">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="Fractorium.qrc">
|
||||
<normaloff>:/Fractorium/Icons/Function-512.png</normaloff>:/Fractorium/Icons/Function-512.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show/Hide Xforms</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show/Hide Xforms</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionAlternateEditorImage">
|
||||
<property name="text">
|
||||
<string>Alternate Editor/Image</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+W</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionResetScale">
|
||||
<property name="text">
|
||||
<string>Reset Scale</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
@ -2,10 +2,12 @@
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "FractoriumSettings.h"
|
||||
#include "PaletteTableWidgetItem.h"
|
||||
|
||||
/// <summary>
|
||||
/// Fractorium global utility functions.
|
||||
/// </summary>
|
||||
#define PALETTE_CELL_HEIGHT 16
|
||||
|
||||
/// <summary>
|
||||
/// Setup a spinner to be placed in a table cell.
|
||||
@ -368,6 +370,116 @@ static void HandleDeviceTableCheckChanged(QTableWidget* table, int row, int col)
|
||||
primaryItem->setCheckState(Qt::Checked);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a row in a table to represent a palette.
|
||||
/// This will place the palette name as a string value in the first column,
|
||||
/// and a QPixmap representing the palette in the second column.
|
||||
/// </summary>
|
||||
/// <param name="paletteTable">The table write to the row to</param>
|
||||
/// <param name="palette">A pointer to the palette to write to the row</param>
|
||||
/// <param name="row">The row to write the palette to</param>
|
||||
static void AddPaletteToTable(QTableWidget* paletteTable, Palette<float>* palette, int row)
|
||||
{
|
||||
auto v = palette->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);
|
||||
auto nameCol = new QTableWidgetItem(palette->m_Name.c_str());
|
||||
nameCol->setToolTip(palette->m_Name.c_str());
|
||||
paletteTable->setItem(row, 0, nameCol);
|
||||
QImage image(v.data(), int(palette->Size()), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);
|
||||
auto paletteItem = new PaletteTableWidgetItem(palette);
|
||||
paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image));
|
||||
paletteItem->setFlags(paletteItem->flags() & ~Qt::ItemIsEditable);
|
||||
paletteTable->setItem(row, 1, paletteItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a palette Xml file and populate the palette table with the contents.
|
||||
/// This will clear any previous contents.
|
||||
/// Called upon initialization, palette combo index change, and controller type change.
|
||||
/// </summary>
|
||||
/// <param name="s">The name of the palette file without the path</param>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
static bool FillPaletteTable(const string& s, QTableWidget* paletteTable, PaletteList<float>& paletteList)
|
||||
{
|
||||
if (!s.empty())//This occasionally seems to get called with an empty string for reasons unknown.
|
||||
{
|
||||
if (auto palettes = paletteList.GetPaletteListByFilename(s))
|
||||
{
|
||||
paletteTable->clear();
|
||||
paletteTable->blockSignals(true);
|
||||
paletteTable->setRowCount(int(palettes->size()));
|
||||
//Headers get removed when clearing, so must re-create here.
|
||||
auto nameHeader = new QTableWidgetItem("Name");
|
||||
auto paletteHeader = new QTableWidgetItem("Palette");
|
||||
nameHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
paletteHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
paletteTable->setHorizontalHeaderItem(0, nameHeader);
|
||||
paletteTable->setHorizontalHeaderItem(1, paletteHeader);
|
||||
|
||||
//Palette list table.
|
||||
for (auto i = 0; i < palettes->size(); i++)
|
||||
if (auto palette = &(*palettes)[i])
|
||||
AddPaletteToTable(paletteTable, palette, i);
|
||||
|
||||
paletteTable->blockSignals(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default search paths for config and palette files.
|
||||
/// </summary>
|
||||
/// <returns>vector<QString> of paths</returns>
|
||||
static vector<QString> GetDefaultPaths()
|
||||
{
|
||||
static vector<QString> paths =
|
||||
{
|
||||
(QDir::homePath() + "/AppData/Roaming/Fractorium").toLocal8Bit().data(),
|
||||
#ifndef _WIN32
|
||||
QString("/usr/share/fractorium").toLocal8Bit().data(),
|
||||
QString("/usr/local/share/fractorium").toLocal8Bit().data(),
|
||||
(QDir::homePath() + "/.config/fractorium").toLocal8Bit().data(),
|
||||
#endif
|
||||
QDir::currentPath().toLocal8Bit().data(),
|
||||
QCoreApplication::applicationDirPath().toLocal8Bit().data()
|
||||
};
|
||||
return paths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default user path for config and palette files.
|
||||
/// </summary>
|
||||
/// <returns>vector<QString> of paths</returns>
|
||||
static QString GetDefaultUserPath()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (QDir::homePath() + "/AppData/Roaming/Fractorium").toLocal8Bit().data();
|
||||
#else
|
||||
return (QDir::homePath() + "/.config/fractorium").toLocal8Bit().data();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the first flam3-palettes.xml file in the default search paths.
|
||||
/// </summary>
|
||||
/// <returns>The full path and filename if found, else empty string.</returns>
|
||||
static QString FindFirstDefaultPalette()
|
||||
{
|
||||
auto paths = GetDefaultPaths();
|
||||
|
||||
for (auto& path : paths)
|
||||
{
|
||||
auto full = path + "/flam3-palettes.xml";
|
||||
|
||||
if (QFile::exists(full))
|
||||
return full;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The basic style that is needed for things to look right, this varies by OS.
|
||||
/// </summary>
|
||||
|
@ -42,33 +42,38 @@ FractoriumEmberController<T>::FractoriumEmberController(Fractorium* fractorium)
|
||||
: FractoriumEmberControllerBase(fractorium),
|
||||
m_VariationList(VariationList<T>::Instance())
|
||||
{
|
||||
bool b = false;
|
||||
size_t b = 0;
|
||||
m_GLController = make_unique<GLEmberController<T>>(fractorium, fractorium->ui.GLDisplay, this);
|
||||
m_LibraryPreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.LibraryTree, m_EmberFile);
|
||||
m_SequencePreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.SequenceTree, m_SequenceFile);
|
||||
m_PaletteList.Clear();
|
||||
m_Fractorium->ui.PaletteFilenameCombo->clear();
|
||||
//Initial combo change event to fill the palette table will be called automatically later.
|
||||
//Look hard for a palette.
|
||||
static vector<string> paths =
|
||||
{
|
||||
QDir::currentPath().toLocal8Bit().data(),
|
||||
QDir::homePath().toLocal8Bit().data(),
|
||||
QCoreApplication::applicationDirPath().toLocal8Bit().data(),
|
||||
QString(QDir::homePath() + "/.config/fractorium").toLocal8Bit().data(),
|
||||
QString("/usr/share/fractorium").toLocal8Bit().data(),
|
||||
QString("/usr/local/share/fractorium").toLocal8Bit().data()
|
||||
};
|
||||
auto paths = GetDefaultPaths();
|
||||
|
||||
for (auto& path : paths)
|
||||
b |= InitPaletteList(path);
|
||||
|
||||
if (b)
|
||||
{
|
||||
if (b = InitPaletteList(path))
|
||||
{
|
||||
m_SheepTools = make_unique<SheepTools<T, float>>(m_PaletteList.Name(0), new EmberNs::Renderer<T, float>());
|
||||
break;
|
||||
}
|
||||
m_SheepTools = make_unique<SheepTools<T, float>>(m_PaletteList.Name(0), new EmberNs::Renderer<T, float>());
|
||||
}
|
||||
else
|
||||
{
|
||||
QString allPaths;
|
||||
|
||||
for (auto& path : paths)
|
||||
allPaths += path + "\r\n";
|
||||
|
||||
allPaths = QString("No palettes found in paths:\r\n") + allPaths + "\r\nExiting.";
|
||||
std::runtime_error ex(allPaths.toStdString());
|
||||
throw ex;
|
||||
}
|
||||
|
||||
if (!b)
|
||||
throw "No palettes found, exiting.";
|
||||
if (m_PaletteList.Size() >= 1)//Only add the user palette if the folder already had a palette, which means we'll be using this folder.
|
||||
if (m_PaletteList.AddEmptyPaletteFile((GetDefaultUserPath() + "/user-palettes.xml").toStdString()))
|
||||
m_Fractorium->ui.PaletteFilenameCombo->addItem("user-palettes.xml");
|
||||
|
||||
BackgroundChanged(QColor(0, 0, 0));//Default to black.
|
||||
ClearUndo();
|
||||
@ -146,18 +151,7 @@ void FractoriumEmberController<T>::SetEmber(size_t index, bool verbatim)
|
||||
{
|
||||
if (index < m_EmberFile.Size())
|
||||
{
|
||||
if (auto top = m_Fractorium->ui.LibraryTree->topLevelItem(0))
|
||||
{
|
||||
for (int i = 0; i < top->childCount(); i++)
|
||||
{
|
||||
if (auto emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
|
||||
{
|
||||
emberItem->setSelected(i == index);
|
||||
emberItem->setCheckState(0, i == index ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Fractorium->SelectLibraryItem(index);
|
||||
ClearUndo();
|
||||
SetEmber(*m_EmberFile.Get(index), verbatim, true);
|
||||
}
|
||||
@ -335,6 +329,13 @@ void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool v
|
||||
static EmberToXml<T> writer;//Save parameters of last full render just in case there is a crash.
|
||||
#ifdef _WIN32
|
||||
string filename = "last.flame";
|
||||
#elif defined(__APPLE__)
|
||||
QDir dir(QDir::applicationDirPath());
|
||||
|
||||
if (!dir.exists())
|
||||
dir.mkpath(".");
|
||||
|
||||
string filename = QDir::applicationDirPath().toStdString() + "/last.flame";
|
||||
#else
|
||||
QDir dir(QDir::homePath() + "/.config/fractorium");
|
||||
|
||||
|
@ -49,6 +49,8 @@ template <typename T> class TreePreviewRenderer;
|
||||
/// </summary>
|
||||
class FractoriumEmberControllerBase : public RenderCallback
|
||||
{
|
||||
friend Fractorium;
|
||||
|
||||
public:
|
||||
FractoriumEmberControllerBase(Fractorium* fractorium);
|
||||
FractoriumEmberControllerBase(const FractoriumEmberControllerBase& controller) = delete;
|
||||
@ -194,13 +196,15 @@ public:
|
||||
double LockedX() { return m_LockedX; }
|
||||
double LockedY() { return m_LockedY; }
|
||||
void LockedScale(double scale) { m_LockedScale = scale; }
|
||||
virtual void LockAffineScaleCheckBoxStateChanged(int state) { }
|
||||
virtual void InitLockedScale() { }
|
||||
|
||||
//Xforms Color.
|
||||
virtual void XformColorIndexChanged(double d, bool updateRender) { }
|
||||
virtual void XformScrollColorIndexChanged(int d) { }
|
||||
virtual void RandomColorIndicesButtonClicked() { }
|
||||
virtual void ToggleColorIndicesButtonClicked() { }
|
||||
virtual void RandomColorSpeedButtonClicked() { }
|
||||
virtual void ToggleColorSpeedButtonClicked() { }
|
||||
virtual void XformColorSpeedChanged(double d) { }
|
||||
virtual void XformOpacityChanged(double d) { }
|
||||
virtual void XformDirectColorChanged(double d) { }
|
||||
@ -222,11 +226,13 @@ public:
|
||||
virtual void RandomXaos() { }
|
||||
|
||||
//Palette.
|
||||
virtual size_t InitPaletteList(const string& s) { return 0; }
|
||||
virtual size_t InitPaletteList(const QString& s) { return 0; }
|
||||
virtual bool FillPaletteTable(const string& s) { return false; }
|
||||
virtual void ApplyPaletteToEmber() { }
|
||||
virtual void PaletteAdjust() { }
|
||||
virtual void PaletteCellClicked(int row, int col) { }
|
||||
virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) { }
|
||||
virtual void PaletteEditorButtonClicked() { }
|
||||
QImage& FinalPaletteImage() { return m_FinalPaletteImage; }
|
||||
|
||||
//Info.
|
||||
@ -287,6 +293,8 @@ protected:
|
||||
unique_ptr<EmberNs::RendererBase> m_Renderer;
|
||||
QTIsaac<ISAAC_SIZE, ISAAC_INT> m_Rand;
|
||||
Fractorium* m_Fractorium;
|
||||
Palette<float> m_TempPalette;
|
||||
PaletteList<float> m_PaletteList;
|
||||
std::unique_ptr<QTimer> m_RenderTimer;
|
||||
std::unique_ptr<QTimer> m_RenderRestartTimer;
|
||||
shared_ptr<OpenCLInfo> m_Info = OpenCLInfo::Instance();
|
||||
@ -451,8 +459,9 @@ public:
|
||||
virtual void ResetXformsAffine(bool pre) override;
|
||||
virtual void RandomXformsAffine(bool pre) override;
|
||||
virtual void FillBothAffines() override;
|
||||
virtual void LockAffineScaleCheckBoxStateChanged(int state) override;
|
||||
virtual void InitLockedScale() override;
|
||||
void FillAffineWithXform(Xform<T>* xform, bool pre);
|
||||
void ChangeLockedScale(T value);
|
||||
T AffineScaleCurrentToLocked();
|
||||
T AffineScaleLockedToCurrent();
|
||||
|
||||
@ -461,6 +470,8 @@ public:
|
||||
virtual void XformScrollColorIndexChanged(int d) override;
|
||||
virtual void RandomColorIndicesButtonClicked() override;
|
||||
virtual void ToggleColorIndicesButtonClicked() override;
|
||||
virtual void RandomColorSpeedButtonClicked() override;
|
||||
virtual void ToggleColorSpeedButtonClicked() override;
|
||||
virtual void XformColorSpeedChanged(double d) override;
|
||||
virtual void XformOpacityChanged(double d) override;
|
||||
virtual void XformDirectColorChanged(double d) override;
|
||||
@ -486,11 +497,13 @@ public:
|
||||
bool XformCheckboxAt(Xform<T>* xform, std::function<void(QCheckBox*)> func);
|
||||
|
||||
//Palette.
|
||||
virtual size_t InitPaletteList(const string& s) override;
|
||||
virtual size_t InitPaletteList(const QString& s) override;
|
||||
virtual bool FillPaletteTable(const string& s) override;
|
||||
virtual void ApplyPaletteToEmber() override;
|
||||
virtual void PaletteAdjust() override;
|
||||
virtual void PaletteCellClicked(int row, int col) override;
|
||||
virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) override;
|
||||
virtual void PaletteEditorButtonClicked() override;
|
||||
|
||||
//Info.
|
||||
virtual void FillSummary() override;
|
||||
@ -522,7 +535,7 @@ private:
|
||||
QString MakeXformCaption(size_t i);
|
||||
|
||||
//Palette.
|
||||
void UpdateAdjustedPaletteGUI(Palette<T>& palette);
|
||||
void UpdateAdjustedPaletteGUI(Palette<float>& palette);
|
||||
|
||||
//Rendering/progress.
|
||||
void Update(std::function<void (void)> func, bool updateRender = true, eProcessAction action = eProcessAction::FULL_RENDER);
|
||||
@ -540,8 +553,6 @@ private:
|
||||
deque<Ember<T>> m_UndoList;
|
||||
vector<Xform<T>> m_CopiedXforms;
|
||||
Xform<T> m_CopiedFinalXform;
|
||||
Palette<T> m_TempPalette;
|
||||
PaletteList<T> m_PaletteList;
|
||||
shared_ptr<VariationList<T>> m_VariationList;
|
||||
unique_ptr<SheepTools<T, float>> m_SheepTools;
|
||||
unique_ptr<GLEmberController<T>> m_GLController;
|
||||
|
@ -43,6 +43,25 @@ void Fractorium::InitLibraryUI()
|
||||
connect(ui.SequenceRandomBlendMaxFramesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomBlendMaxFramesSpinBoxChanged(int)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select the item in the library tree specified by the passed in index.
|
||||
/// </summary>
|
||||
/// <param name="index">The 0-based index of the item in the library tree to select</param>
|
||||
void Fractorium::SelectLibraryItem(size_t index)
|
||||
{
|
||||
if (auto top = ui.LibraryTree->topLevelItem(0))
|
||||
{
|
||||
for (int i = 0; i < top->childCount(); i++)
|
||||
{
|
||||
if (auto emberItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(i)))
|
||||
{
|
||||
emberItem->setSelected(i == index);
|
||||
emberItem->setCheckState(0, i == index ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the currently selected ember in the library tree.
|
||||
/// </summary>
|
||||
@ -153,9 +172,7 @@ void FractoriumEmberController<T>::FillLibraryTree(int selectIndex)
|
||||
tree->blockSignals(false);
|
||||
|
||||
if (selectIndex != -1)
|
||||
if (auto top = tree->topLevelItem(0))
|
||||
if (auto emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(selectIndex)))
|
||||
emberItem->setSelected(true);
|
||||
m_Fractorium->SelectLibraryItem(selectIndex);
|
||||
|
||||
m_Fractorium->SyncFileCountToSequenceCount();
|
||||
QCoreApplication::flush();
|
||||
@ -568,12 +585,13 @@ void FractoriumEmberController<T>::SequenceGenerateButtonClicked()
|
||||
vector<pair<size_t, size_t>> devices;//Dummy.
|
||||
EmberReport emberReport;
|
||||
ostringstream os;
|
||||
string palettePath =
|
||||
#ifdef _WIN32
|
||||
"./flam3-palettes.xml";
|
||||
#else
|
||||
"~/.config/fractorium";
|
||||
#endif
|
||||
string palettePath = FindFirstDefaultPalette().toStdString();
|
||||
|
||||
if (palettePath.empty())//This should never happen because the program requires a palette file to run this far.
|
||||
{
|
||||
QMessageBox::warning(nullptr, "Sequence", "No flam3-palettes.xml file found, sequence will not be generated.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!randRot && !randBlend)
|
||||
{
|
||||
@ -700,12 +718,15 @@ void Fractorium::OnSequenceGenerateButtonClicked(bool checked) { m_Controller->S
|
||||
/// <param name="checked">Ignored.</param>
|
||||
void Fractorium::OnSequenceRenderButtonClicked(bool checked)
|
||||
{
|
||||
//First completely stop what the current rendering process is doing.
|
||||
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
|
||||
m_Controller->StopAllPreviewRenderers();
|
||||
m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file.
|
||||
m_RenderStatusLabel->setText("Renderer stopped.");
|
||||
m_FinalRenderDialog->Show(true);//Show with a bool specifying that it came from the sequence generator.
|
||||
if (ui.SequenceTree->topLevelItemCount() > 0)
|
||||
{
|
||||
//First completely stop what the current rendering process is doing.
|
||||
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
|
||||
m_Controller->StopAllPreviewRenderers();
|
||||
m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file.
|
||||
m_RenderStatusLabel->setText("Renderer stopped.");
|
||||
m_FinalRenderDialog->Show(true);//Show with a bool specifying that it came from the sequence generator.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -825,5 +846,5 @@ void Fractorium::SyncSequenceSettings()
|
||||
template class FractoriumEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FractoriumEmberController<double>;
|
||||
template class FractoriumEmberController<double>;
|
||||
#endif
|
||||
|
@ -27,7 +27,9 @@ void Fractorium::InitMenusUI()
|
||||
connect(ui.ActionPasteSelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteSelectedXforms(bool)), Qt::QueuedConnection);
|
||||
ui.ActionPasteSelectedXforms->setEnabled(false);
|
||||
//View menu.
|
||||
connect(ui.ActionResetWorkspace, SIGNAL(triggered(bool)), this, SLOT(OnActionResetWorkspace(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionResetWorkspace, SIGNAL(triggered(bool)), this, SLOT(OnActionResetWorkspace(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionAlternateEditorImage, SIGNAL(triggered(bool)), this, SLOT(OnActionAlternateEditorImage(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionResetScale, SIGNAL(triggered(bool)), this, SLOT(OnActionResetScale(bool)), Qt::QueuedConnection);
|
||||
//Tools menu.
|
||||
connect(ui.ActionAddReflectiveSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddReflectiveSymmetry(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionAddRotationalSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddRotationalSymmetry(bool)), Qt::QueuedConnection);
|
||||
@ -698,6 +700,38 @@ void Fractorium::OnActionResetWorkspace(bool checked)
|
||||
ui.LibraryDockWidget->show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alternate between Editor/Image.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionAlternateEditorImage(bool checked)
|
||||
{
|
||||
if (DrawImage())
|
||||
{
|
||||
ui.ActionDrawImage->setChecked(false);
|
||||
ui.ActionDrawXforms->setChecked(true);
|
||||
ui.ActionDrawGrid->setChecked(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui.ActionDrawXforms->setChecked(false);
|
||||
ui.ActionDrawGrid->setChecked(false);
|
||||
ui.ActionDrawImage->setChecked(true);
|
||||
}
|
||||
|
||||
ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the scale used to draw affines, which was adjusted by zooming with the Alt key pressed.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionResetScale(bool checked)
|
||||
{
|
||||
m_Controller->InitLockedScale();
|
||||
ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add reflective symmetry to the current ember.
|
||||
/// Resets the rendering process.
|
||||
@ -877,5 +911,5 @@ void Fractorium::OnActionAbout(bool checked)
|
||||
template class FractoriumEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FractoriumEmberController<double>;
|
||||
template class FractoriumEmberController<double>;
|
||||
#endif
|
||||
|
@ -2,8 +2,6 @@
|
||||
#include "Fractorium.h"
|
||||
#include "PaletteTableWidgetItem.h"
|
||||
|
||||
#define PALETTE_CELL_HEIGHT 16
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the palette UI.
|
||||
/// </summary>
|
||||
@ -25,8 +23,10 @@ void Fractorium::InitPaletteUI()
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 3, m_PaletteContrastSpin, spinHeight, -100, 100, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 3, m_PaletteBlurSpin, spinHeight, 0, 127, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 3, m_PaletteFrequencySpin, spinHeight, 1, 10, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 1, 1, 1);
|
||||
connect(ui.PaletteRandomSelect, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomSelectButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PaletteRandomAdjust, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomAdjustButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PaletteRandomSelectButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomSelectButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PaletteRandomAdjustButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomAdjustButtonClicked(bool)), Qt::QueuedConnection);
|
||||
//Palette editor.
|
||||
connect(ui.PaletteEditorButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteEditorButtonClicked(bool)), Qt::QueuedConnection);
|
||||
//Preview table.
|
||||
palettePreviewTable->setRowCount(1);
|
||||
palettePreviewTable->setColumnWidth(1, 260);//256 plus small margin on each side.
|
||||
@ -38,13 +38,13 @@ void Fractorium::InitPaletteUI()
|
||||
connect(ui.PaletteFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteFilterClearButtonClicked(bool)));
|
||||
paletteTable->setColumnWidth(1, 260);//256 plus small margin on each side.
|
||||
paletteTable->horizontalHeader()->setSectionsClickable(true);
|
||||
connect(paletteTable->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(OnPaletteHeaderSectionClicked(int)), Qt::QueuedConnection);
|
||||
connect(ui.ResetCurvesButton, SIGNAL(clicked(bool)), this, SLOT(OnResetCurvesButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesView, SIGNAL(PointChangedSignal(int, int, const QPointF&)), this, SLOT(OnCurvesPointChanged(int, int, const QPointF&)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesAllRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesRedRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesRedRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesGreenRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesGreenRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesBlueRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesBlueRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(paletteTable->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(OnPaletteHeaderSectionClicked(int)), Qt::QueuedConnection);
|
||||
connect(ui.ResetCurvesButton, SIGNAL(clicked(bool)), this, SLOT(OnResetCurvesButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesView, SIGNAL(PointChangedSignal(int, int, const QPointF&)), this, SLOT(OnCurvesPointChanged(int, int, const QPointF&)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesAllRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesRedRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesRedRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesGreenRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesGreenRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurvesBlueRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesBlueRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -55,20 +55,28 @@ void Fractorium::InitPaletteUI()
|
||||
/// <param name="s">The full path to the palette files folder</param>
|
||||
/// <returns>The number of palettes successfully added</returns>
|
||||
template <typename T>
|
||||
size_t FractoriumEmberController<T>::InitPaletteList(const string& s)
|
||||
size_t FractoriumEmberController<T>::InitPaletteList(const QString& s)
|
||||
{
|
||||
QDirIterator it(s.c_str(), QStringList() << "*.xml", QDir::Files, QDirIterator::FollowSymlinks);
|
||||
m_PaletteList.Clear();
|
||||
m_Fractorium->ui.PaletteFilenameCombo->clear();
|
||||
m_Fractorium->ui.PaletteFilenameCombo->setProperty("path", QString::fromStdString(s));
|
||||
QDirIterator it(s, QStringList() << "*.xml" << "*.ugr" << "*.gradient" << "*.gradients", QDir::Files, QDirIterator::FollowSymlinks);
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
auto path = it.next().toStdString();
|
||||
auto path = it.next();
|
||||
auto qfilename = it.fileName();
|
||||
|
||||
if (m_PaletteList.Add(path))
|
||||
m_Fractorium->ui.PaletteFilenameCombo->addItem(qfilename);
|
||||
try
|
||||
{
|
||||
if (QFile::exists(path) && m_PaletteList.Add(path.toStdString()))
|
||||
m_Fractorium->ui.PaletteFilenameCombo->addItem(qfilename);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Palette Parsing Error", QString::fromStdString(e.what()));
|
||||
}
|
||||
catch (const char* e)
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Palette Parsing Error", e);
|
||||
}
|
||||
}
|
||||
|
||||
return m_PaletteList.Size();
|
||||
@ -79,7 +87,7 @@ size_t FractoriumEmberController<T>::InitPaletteList(const string& s)
|
||||
/// This will clear any previous contents.
|
||||
/// Called upon initialization, palette combo index change, and controller type change.
|
||||
/// </summary>
|
||||
/// <param name="s">The name to the palette file without the path</param>
|
||||
/// <param name="s">The name of the palette file without the path</param>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
template <typename T>
|
||||
bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
|
||||
@ -87,38 +95,10 @@ bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
|
||||
if (!s.empty())//This occasionally seems to get called with an empty string for reasons unknown.
|
||||
{
|
||||
auto paletteTable = m_Fractorium->ui.PaletteListTable;
|
||||
m_CurrentPaletteFilePath = m_Fractorium->ui.PaletteFilenameCombo->property("path").toString().toStdString() + "/" + s;
|
||||
m_CurrentPaletteFilePath = s;
|
||||
|
||||
if (int paletteSize = int(m_PaletteList.Size(m_CurrentPaletteFilePath)))
|
||||
if (::FillPaletteTable(m_CurrentPaletteFilePath, paletteTable, m_PaletteList))
|
||||
{
|
||||
paletteTable->clear();
|
||||
paletteTable->blockSignals(true);
|
||||
paletteTable->setRowCount(paletteSize);
|
||||
//Headers get removed when clearing, so must re-create here.
|
||||
auto nameHeader = new QTableWidgetItem("Name");
|
||||
auto paletteHeader = new QTableWidgetItem("Palette");
|
||||
nameHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
paletteHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
paletteTable->setHorizontalHeaderItem(0, nameHeader);
|
||||
paletteTable->setHorizontalHeaderItem(1, paletteHeader);
|
||||
|
||||
//Palette list table.
|
||||
for (auto i = 0; i < paletteSize; i++)
|
||||
{
|
||||
if (auto p = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, i))
|
||||
{
|
||||
auto v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);
|
||||
auto nameCol = new QTableWidgetItem(p->m_Name.c_str());
|
||||
nameCol->setToolTip(p->m_Name.c_str());
|
||||
paletteTable->setItem(i, 0, nameCol);
|
||||
QImage image(v.data(), int(p->Size()), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);
|
||||
auto paletteItem = new PaletteTableWidgetItem<T>(p);
|
||||
paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image));
|
||||
paletteTable->setItem(i, 1, paletteItem);
|
||||
}
|
||||
}
|
||||
|
||||
paletteTable->blockSignals(false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -126,15 +106,24 @@ bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
|
||||
vector<string> errors = m_PaletteList.ErrorReport();
|
||||
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
|
||||
m_Fractorium->ShowCritical("Palette Read Error", "Could not load palette file, all images will be black. See info tab for details.");
|
||||
m_PaletteList.ClearErrorReport();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the palette table with the passed in string.
|
||||
/// Called when the palette name combo box changes.
|
||||
/// </summary>
|
||||
/// <param name="text">The full path to the palette file</param>
|
||||
void Fractorium::OnPaletteFilenameComboChanged(const QString& text)
|
||||
{
|
||||
m_Controller->FillPaletteTable(text.toStdString());
|
||||
auto s = text.toStdString();
|
||||
m_Controller->FillPaletteTable(s);
|
||||
auto fullname = m_Controller->m_PaletteList.GetFullPathFromFilename(s);
|
||||
ui.PaletteFilenameCombo->setToolTip(QString::fromStdString(fullname));
|
||||
ui.PaletteListTable->sortItems(0, m_PaletteSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||
}
|
||||
|
||||
@ -161,7 +150,7 @@ void FractoriumEmberController<T>::ApplyPaletteToEmber()
|
||||
/// <param name="palette">The palette to use</param>
|
||||
/// <param name="paletteName">Name of the palette</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::UpdateAdjustedPaletteGUI(Palette<T>& palette)
|
||||
void FractoriumEmberController<T>::UpdateAdjustedPaletteGUI(Palette<float>& palette)
|
||||
{
|
||||
auto xform = CurrentXform();
|
||||
auto palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
|
||||
@ -205,6 +194,20 @@ void FractoriumEmberController<T>::PaletteAdjust()
|
||||
|
||||
void Fractorium::OnPaletteAdjust(int d) { m_Controller->PaletteAdjust(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the passed in palette as the current one,
|
||||
/// applying any adjustments previously specified.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette to assign to the temporary palette</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SetBasePaletteAndAdjust(const Palette<float>& palette)
|
||||
{
|
||||
m_TempPalette = palette;//Deep copy.
|
||||
ApplyPaletteToEmber();//Copy temp palette to ember palette and apply adjustments.
|
||||
UpdateAdjustedPaletteGUI(m_Ember.m_Palette);//Show the adjusted palette.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the selected palette as the current one,
|
||||
/// applying any adjustments previously specified.
|
||||
@ -219,12 +222,8 @@ void Fractorium::OnPaletteAdjust(int d) { m_Controller->PaletteAdjust(); }
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::PaletteCellClicked(int row, int col)
|
||||
{
|
||||
if (auto palette = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, row))
|
||||
{
|
||||
m_TempPalette = *palette;//Deep copy.
|
||||
ApplyPaletteToEmber();//Copy temp palette to ember palette and apply adjustments.
|
||||
UpdateAdjustedPaletteGUI(m_Ember.m_Palette);//Show the adjusted palette.
|
||||
}
|
||||
if (auto palette = m_PaletteList.GetPaletteByFilename(m_CurrentPaletteFilePath, row))
|
||||
SetBasePaletteAndAdjust(*palette);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -237,7 +236,7 @@ void FractoriumEmberController<T>::PaletteCellClicked(int row, int col)
|
||||
/// <param name="col">The table column clicked, ignored</param>
|
||||
void Fractorium::OnPaletteCellClicked(int row, int col)
|
||||
{
|
||||
if (auto item = dynamic_cast<PaletteTableWidgetItemBase*>(ui.PaletteListTable->item(row, 1)))
|
||||
if (auto item = dynamic_cast<PaletteTableWidgetItem*>(ui.PaletteListTable->item(row, 1)))
|
||||
{
|
||||
auto index = int(item->Index());
|
||||
|
||||
@ -311,6 +310,85 @@ void Fractorium::OnPaletteRandomAdjustButtonClicked(bool checked)
|
||||
OnPaletteAdjust(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the palette editor dialog.
|
||||
/// Called when the palette editor button is clicked.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::PaletteEditorButtonClicked()
|
||||
{
|
||||
auto ed = m_Fractorium->m_PaletteEditor;
|
||||
Palette<float> prevPal = m_TempPalette;
|
||||
ed->SetPalette(m_TempPalette);
|
||||
|
||||
if (ed->exec() == QDialog::Accepted)
|
||||
{
|
||||
if (!m_Fractorium->PaletteChanged())//If the clicked ok, but never synced, set the palette now.
|
||||
SetBasePaletteAndAdjust(ed->GetPalette(int(256)));
|
||||
}
|
||||
else if (m_Fractorium->PaletteChanged())//They clicked cancel, but synced at least once, restore the previous palette.
|
||||
{
|
||||
SetBasePaletteAndAdjust(prevPal);
|
||||
}
|
||||
|
||||
//If the palette was modifiable, and any palette was changed at least once
|
||||
if (m_Fractorium->m_PaletteFileChanged && m_PaletteList.IsModifiable(m_CurrentPaletteFilePath))
|
||||
{
|
||||
if (!::FillPaletteTable(m_CurrentPaletteFilePath, m_Fractorium->ui.PaletteListTable, m_PaletteList))
|
||||
{
|
||||
vector<string> errors = m_PaletteList.ErrorReport();
|
||||
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
|
||||
m_Fractorium->ShowCritical("Palette Read Error", "Could not re-load modified palette file, all images will be black. See info tab for details.");
|
||||
m_PaletteList.ClearErrorReport();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot called when the palette editor changes the palette and the Sync checkbox is checked.
|
||||
/// </summary>
|
||||
bool Fractorium::PaletteChanged()
|
||||
{
|
||||
return m_PaletteChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the palette editor dialog.
|
||||
/// This creates the palette editor dialog if it has not been created at least once.
|
||||
/// Called when the palette editor button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnPaletteEditorButtonClicked(bool checked)
|
||||
{
|
||||
if (!m_PaletteEditor)
|
||||
{
|
||||
m_PaletteEditor = new PaletteEditor(m_Controller->m_PaletteList, this);
|
||||
connect(m_PaletteEditor, SIGNAL(PaletteChanged()), this, SLOT(OnPaletteEditorColorChanged()), Qt::QueuedConnection);
|
||||
connect(m_PaletteEditor, SIGNAL(PaletteFileChanged()), this, SLOT(OnPaletteEditorFileChanged()), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
m_PaletteChanged = false;
|
||||
m_PaletteFileChanged = false;
|
||||
m_Controller->PaletteEditorButtonClicked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot called every time a color is changed in the palette editor.
|
||||
/// </summary>
|
||||
void Fractorium::OnPaletteEditorColorChanged()
|
||||
{
|
||||
m_PaletteChanged = true;
|
||||
m_Controller->SetBasePaletteAndAdjust(m_PaletteEditor->GetPalette(int(256)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot called every time a palette file is changed in the palette editor.
|
||||
/// </summary>
|
||||
void Fractorium::OnPaletteEditorFileChanged()
|
||||
{
|
||||
m_PaletteFileChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the text in the palette filter text box to only show palettes whose names
|
||||
/// contain the substring.
|
||||
@ -427,7 +505,7 @@ void Fractorium::OnCurvesPointChanged(int curveIndex, int pointIndex, const QPoi
|
||||
/// select a point by putting it on top of all the others.
|
||||
/// Called when the any of the curve color radio buttons are toggled.
|
||||
/// </summary>
|
||||
/// <param name="curveIndex">The curve index, 0-1/</param>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnCurvesAllRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::ALL); }
|
||||
void Fractorium::OnCurvesRedRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::RED); }
|
||||
void Fractorium::OnCurvesGreenRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::GREEN); }
|
||||
@ -457,5 +535,5 @@ void FractoriumEmberController<T>::FillCurvesControl()
|
||||
template class FractoriumEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FractoriumEmberController<double>;
|
||||
template class FractoriumEmberController<double>;
|
||||
#endif
|
||||
|
@ -22,11 +22,12 @@ void Fractorium::InitParamsUI()
|
||||
SetFixedTableHeader(ui.IterationTableHeader->horizontalHeader());
|
||||
SetFixedTableHeader(ui.AnimationTableHeader->horizontalHeader());
|
||||
//Color.
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.05, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaSpin, spinHeight, 1, 9999, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_VibrancySpin, spinHeight, 0, 30, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 0.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_HighlightSpin, spinHeight, -1.0, 10, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHighlightPowerChanged(double)), true, 1.0, -1.0, -1.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.05, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaSpin, spinHeight, 1, 9999, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_VibrancySpin, spinHeight, 0, 30, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 0.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_HighlightSpin, spinHeight, -1.0, 10, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHighlightPowerChanged(double)), true, 1.0, 1.0, -1.0);
|
||||
m_HighlightSpin->DoubleClickLowVal(-1.0);
|
||||
m_GammaThresholdSpin->setDecimals(4);
|
||||
m_BackgroundColorButton = new QPushButton("...", table);
|
||||
m_BackgroundColorButton->setMinimumWidth(21);
|
||||
@ -63,7 +64,8 @@ void Fractorium::InitParamsUI()
|
||||
//Filter.
|
||||
row = 0;
|
||||
table = ui.FilterTable;
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_SpatialFilterWidthSpin, spinHeight, 0.1, 2, 0.1, SIGNAL(valueChanged(double)), SLOT(OnSpatialFilterWidthChanged(double)), true, 1.0, 1.0, 1.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_SpatialFilterWidthSpin, spinHeight, 0.1, 2, 0.1, SIGNAL(valueChanged(double)), SLOT(OnSpatialFilterWidthChanged(double)), true, 1.0, 1.0, 0.1);
|
||||
m_SpatialFilterWidthSpin->DoubleClickLowVal(0.1);
|
||||
comboVals = SpatialFilterCreator<float>::FilterTypes();
|
||||
SetupCombo(table, this, row, 1, m_SpatialFilterTypeCombo, comboVals, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnSpatialFilterTypeComboCurrentIndexChanged(const QString&)));
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DEFilterMinRadiusSpin, spinHeight, 0, 25, 1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterMinRadiusWidthChanged(double)), true, 0, 0, 0);
|
||||
@ -192,7 +194,7 @@ void Fractorium::OnHighlightPowerChanged(double d) { m_Controller->HighlightPowe
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnBackgroundColorButtonClicked(bool checked)
|
||||
{
|
||||
m_ColorDialog->show();
|
||||
m_ColorDialog->exec();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -725,6 +727,7 @@ void FractoriumEmberController<T>::FillParamTablesAndPalette()
|
||||
//Since the controls were cleared above, the adjusted palette will be identical to the base palette.
|
||||
//Callers can set, apply and display palette adjustments after this function exits if needed.
|
||||
UpdateAdjustedPaletteGUI(m_Ember.m_Palette);//Updating the palette GUI will trigger a full render.
|
||||
InitLockedScale();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -803,5 +806,5 @@ void Fractorium::SetScale(double scale)
|
||||
template class FractoriumEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FractoriumEmberController<double>;
|
||||
template class FractoriumEmberController<double>;
|
||||
#endif
|
||||
|
@ -26,41 +26,69 @@
|
||||
#include <QtWidgets>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <deque>
|
||||
#include "qfunctions.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBrush>
|
||||
#include <QCheckBox>
|
||||
#include <QClipboard>
|
||||
#include <QColor>
|
||||
#include <QColorDialog>
|
||||
#include <QComboBox>
|
||||
#include <QConicalGradient>
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDial>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QEvent>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QFont>
|
||||
#include <QFontDialog>
|
||||
#include <QFontMetrics>
|
||||
#include <QFrame>
|
||||
#include <QFuture>
|
||||
#include <QGraphicsView>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHash>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIcon>
|
||||
#include <QImage>
|
||||
#include <QImageReader>
|
||||
#include <QItemDelegate>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QLinearGradient>
|
||||
#include <QLineEdit>
|
||||
#include <QMap>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QMimeData>
|
||||
#include <QModelIndex>
|
||||
#include <QMouseEvent>
|
||||
#include <qopenglfunctions_2_0.h>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPaintEvent>
|
||||
#include <QPixmap>
|
||||
#include <QPoint>
|
||||
#include <QPolygon>
|
||||
#include <QPushButton>
|
||||
#include <QRect>
|
||||
#include <QResizeEvent>
|
||||
#include <QSettings>
|
||||
#include <QSignalMapper>
|
||||
#include <QSize>
|
||||
#include <QSizePolicy>
|
||||
#include <QSpinBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextEdit>
|
||||
#include <QTimer>
|
||||
#include <QToolBar>
|
||||
#include <QTreeWidget>
|
||||
#include <QWheelEvent>
|
||||
#include <QtConcurrentRun>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/QMultiHash>
|
||||
#include <QtCore/QPair>
|
||||
#include <QtCore/QSharedData>
|
||||
@ -68,11 +96,31 @@
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QTextEdit>
|
||||
#include <QTextStream>
|
||||
#include <QtGui/QFont>
|
||||
#include <QtGui/QPalette>
|
||||
#include <QtGui/QSyntaxHighlighter>
|
||||
#include <QThread>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
#include <QToolBar>
|
||||
#include <QToolTip>
|
||||
#include <QTreeWidget>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QVarLengthArray>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVector>
|
||||
#include <QWheelEvent>
|
||||
#include <QWidget>
|
||||
#include <QWidgetAction>
|
||||
|
||||
#define GLM_FORCE_RADIANS 1
|
||||
#define GLM_ENABLE_EXPERIMENTAL 1
|
||||
|
||||
#ifndef __APPLE__
|
||||
#define GLM_FORCE_INLINE 1
|
||||
#endif
|
||||
|
||||
#include "glm/glm.hpp"
|
||||
#include "glm/gtc/matrix_transform.hpp"
|
||||
|
@ -109,6 +109,12 @@ void FractoriumSettings::Double(bool b) { setValue(DOUBLEPRECISION, b);
|
||||
bool FractoriumSettings::ShowAllXforms() { return value(SHOWALLXFORMS).toBool(); }
|
||||
void FractoriumSettings::ShowAllXforms(bool b) { setValue(SHOWALLXFORMS, b); }
|
||||
|
||||
bool FractoriumSettings::ShowXforms() { return value(SHOWXFORMS, QVariant::fromValue(true)).toBool(); }
|
||||
void FractoriumSettings::ShowXforms(bool b) { setValue(SHOWXFORMS, b); }
|
||||
|
||||
bool FractoriumSettings::ShowGrid() { return value(SHOWGRID, QVariant::fromValue(true)).toBool(); }
|
||||
void FractoriumSettings::ShowGrid(bool b) { setValue(SHOWGRID, b); }
|
||||
|
||||
bool FractoriumSettings::ToggleType() { return value(TOGGLETYPE).toBool(); }
|
||||
void FractoriumSettings::ToggleType(bool b) { setValue(TOGGLETYPE, b); }
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
#define DOUBLEPRECISION "render/dp64"
|
||||
#define CONTUPDATE "render/continuousupdate"
|
||||
#define SHOWALLXFORMS "render/dragshowallxforms"
|
||||
#define SHOWXFORMS "render/showxforms"
|
||||
#define SHOWGRID "render/showgrid"
|
||||
#define TOGGLETYPE "render/toggletype"
|
||||
#define DEVICES "render/devices"
|
||||
#define THREADCOUNT "render/threadcount"
|
||||
@ -112,6 +114,12 @@ public:
|
||||
bool ShowAllXforms();
|
||||
void ShowAllXforms(bool b);
|
||||
|
||||
bool ShowXforms();
|
||||
void ShowXforms(bool b);
|
||||
|
||||
bool ShowGrid();
|
||||
void ShowGrid(bool b);
|
||||
|
||||
bool ToggleType();
|
||||
void ToggleType(bool b);
|
||||
|
||||
|
@ -14,14 +14,26 @@ void Fractorium::InitToolbarUI()
|
||||
spGroup->addAction(ui.ActionSP);
|
||||
spGroup->addAction(ui.ActionDP);
|
||||
SyncOptionsToToolbar();
|
||||
ui.ActionDrawImage->setChecked(true);
|
||||
connect(ui.ActionCpu, SIGNAL(triggered(bool)), this, SLOT(OnActionCpu(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionCL, SIGNAL(triggered(bool)), this, SLOT(OnActionCL(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionSP, SIGNAL(triggered(bool)), this, SLOT(OnActionSP(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionDP, SIGNAL(triggered(bool)), this, SLOT(OnActionDP(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionStyle, SIGNAL(triggered(bool)), this, SLOT(OnActionStyle(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionStartStopRenderer, SIGNAL(triggered(bool)), this, SLOT(OnActionStartStopRenderer(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionDrawXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawXforms(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionDrawImage, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawImage(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionDrawGrid, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawGrid(bool)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GUI wrapper functions, getters only.
|
||||
/// </summary>
|
||||
|
||||
bool Fractorium::DrawXforms() { return ui.ActionDrawXforms->isChecked(); }
|
||||
bool Fractorium::DrawImage() { return ui.ActionDrawImage->isChecked(); }
|
||||
bool Fractorium::DrawGrid() { return ui.ActionDrawGrid->isChecked(); }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the CPU render option on the toolbar is clicked.
|
||||
/// </summary>
|
||||
@ -80,7 +92,11 @@ void Fractorium::OnActionDP(bool checked)
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionStyle(bool checked)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
m_QssDialog->exec();
|
||||
#else
|
||||
m_QssDialog->show();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -105,6 +121,42 @@ void Fractorium::OnActionStartStopRenderer(bool checked)
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle whether to show the affines.
|
||||
/// Called when the editor image button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Check state, show editor if true, else hide.</param>
|
||||
void Fractorium::OnActionDrawXforms(bool checked)
|
||||
{
|
||||
if (!ui.ActionDrawImage->isChecked() && !ui.ActionDrawXforms->isChecked())
|
||||
ui.ActionDrawImage->setChecked(true);
|
||||
|
||||
ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle whether to show the image.
|
||||
/// Called when the image button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Check state, show image if true, else hide.</param>
|
||||
void Fractorium::OnActionDrawImage(bool checked)
|
||||
{
|
||||
if (!ui.ActionDrawImage->isChecked() && !ui.ActionDrawXforms->isChecked())
|
||||
ui.ActionDrawXforms->setChecked(true);
|
||||
|
||||
ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle whether to show the grid.
|
||||
/// Called when the grid image button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Check state, show grid if true, else hide.</param>
|
||||
void Fractorium::OnActionDrawGrid(bool checked)
|
||||
{
|
||||
ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sync options data to the check state of the toolbar buttons.
|
||||
/// This does not trigger a clicked() event.
|
||||
@ -139,4 +191,7 @@ void Fractorium::SyncOptionsToToolbar()
|
||||
ui.ActionSP->setChecked(true);
|
||||
ui.ActionDP->setChecked(false);
|
||||
}
|
||||
|
||||
ui.ActionDrawGrid->setChecked(m_Settings->ShowGrid());
|
||||
ui.ActionDrawXforms->setChecked(m_Settings->ShowXforms());
|
||||
}
|
@ -70,7 +70,7 @@ void FractoriumEmberController<T>::XaosChanged(int x, int y, double val)
|
||||
|
||||
void Fractorium::OnXaosChanged(double d)
|
||||
{
|
||||
if (auto senderSpinBox = qobject_cast<DoubleSpinBox*>(this->sender()))
|
||||
if (auto senderSpinBox = qobject_cast<DoubleSpinBox*>(sender()))
|
||||
{
|
||||
auto p = senderSpinBox->property("tableindex").toPoint();
|
||||
m_Controller->XaosChanged(p.x(), p.y(), d);
|
||||
|
@ -9,7 +9,6 @@ void Fractorium::InitXformsAffineUI()
|
||||
int affinePrec = 6, spinHeight = 20;
|
||||
double affineStep = 0.01, affineMin = std::numeric_limits<double>::lowest(), affineMax = std::numeric_limits<double>::max();
|
||||
auto table = ui.PreAffineTable;
|
||||
connect(ui.LockAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnLockAffineScaleCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
table->verticalHeader()->setVisible(true);//The designer continually clobbers these values, so must manually set them here.
|
||||
table->horizontalHeader()->setVisible(true);
|
||||
table->verticalHeader()->setSectionsClickable(true);
|
||||
@ -155,20 +154,29 @@ void Fractorium::InitXformsAffineUI()
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle whether to lock the visual scale of the affine spinners.
|
||||
/// Called when the user checks LockAffineCheckBox.
|
||||
/// Set the scale used for drawing the affines to a default value.
|
||||
/// </summary>
|
||||
/// <param name="state">True if checked, else false.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::LockAffineScaleCheckBoxStateChanged(int state)
|
||||
void FractoriumEmberController<T>::InitLockedScale()
|
||||
{
|
||||
m_LockedScale = m_Ember.m_PixelsPerUnit;
|
||||
m_LockedScale = (T)std::min<size_t>(m_Ember.m_FinalRasW, m_Ember.m_FinalRasH) / 4.0;
|
||||
m_LockedX = m_Ember.m_CenterX;
|
||||
m_LockedY = m_Ember.m_CenterY;
|
||||
m_Fractorium->ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
void Fractorium::OnLockAffineScaleCheckBoxStateChanged(int state) { m_Controller->LockAffineScaleCheckBoxStateChanged(state); }
|
||||
/// <summary>
|
||||
/// Multiply the scale used for drawing the affines by the passed in value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to scale by</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ChangeLockedScale(T value)
|
||||
{
|
||||
T min_size = 25.0;
|
||||
m_LockedScale *= value;
|
||||
|
||||
if (m_LockedScale < min_size)
|
||||
m_LockedScale = min_size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the value needed to multiply the current scale by to get back to the locked scale.
|
||||
@ -177,10 +185,7 @@ void Fractorium::OnLockAffineScaleCheckBoxStateChanged(int state) { m_Controller
|
||||
template <typename T>
|
||||
T FractoriumEmberController<T>::AffineScaleCurrentToLocked()
|
||||
{
|
||||
if (m_Fractorium->ui.LockAffineCheckBox->isChecked())
|
||||
return LockedScale() / m_Ember.m_PixelsPerUnit;
|
||||
else
|
||||
return 1;
|
||||
return LockedScale() / m_Ember.m_PixelsPerUnit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -190,10 +195,7 @@ T FractoriumEmberController<T>::AffineScaleCurrentToLocked()
|
||||
template <typename T>
|
||||
T FractoriumEmberController<T>::AffineScaleLockedToCurrent()
|
||||
{
|
||||
if (m_Fractorium->ui.LockAffineCheckBox->isChecked())
|
||||
return m_Ember.m_PixelsPerUnit / LockedScale();
|
||||
else
|
||||
return 1;
|
||||
return m_Ember.m_PixelsPerUnit / LockedScale();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -736,5 +738,5 @@ bool Fractorium::LocalPivot() { return ui.LocalPivotRadio->isChecked();
|
||||
template class FractoriumEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FractoriumEmberController<double>;
|
||||
template class FractoriumEmberController<double>;
|
||||
#endif
|
||||
|
@ -18,6 +18,8 @@ void Fractorium::InitXformsColorUI()
|
||||
connect(ui.XformPaletteRefTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), this, SLOT(OnXformRefPaletteResized(int, int, int)), Qt::QueuedConnection);
|
||||
connect(ui.RandomColorIndicesButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomColorIndicesButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ToggleColorIndicesButton, SIGNAL(clicked(bool)), this, SLOT(OnToggleColorIndicesButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.RandomColorSpeedButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomColorSpeedButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ToggleColorSpeedButton, SIGNAL(clicked(bool)), this, SLOT(OnToggleColorSpeedButtonClicked(bool)), Qt::QueuedConnection);
|
||||
SetupSpinner<DoubleSpinBox, double>(ui.XformColorIndexTable, this, row, 1, m_XformColorIndexSpin, spinHeight, 0, 1, 0.01, SIGNAL(valueChanged(double)), SLOT(OnXformColorIndexChanged(double)), false, 0, 1, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(ui.XformColorValuesTable, this, row, 1, m_XformColorSpeedSpin, spinHeight, -1, 1, 0.1, SIGNAL(valueChanged(double)), SLOT(OnXformColorSpeedChanged(double)), true, 0.5, 0.5, 0.5);
|
||||
SetupSpinner<DoubleSpinBox, double>(ui.XformColorValuesTable, this, row, 1, m_XformOpacitySpin, spinHeight, 0, 1, 0.1, SIGNAL(valueChanged(double)), SLOT(OnXformOpacityChanged(double)), true, 1, 1, 0);
|
||||
@ -104,6 +106,33 @@ void FractoriumEmberController<T>::ToggleColorIndicesButtonClicked()
|
||||
}
|
||||
void Fractorium::OnToggleColorIndicesButtonClicked(bool b) { m_Controller->ToggleColorIndicesButtonClicked(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set all xform color speeds to a random value between 0 and 1, inclusive.
|
||||
/// Called when the Random Color Speed button is clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::RandomColorSpeedButtonClicked()
|
||||
{
|
||||
UpdateXform([&](Xform<T>* xform) { xform->m_ColorSpeed = m_Rand.Frand01<T>(); }, eXformUpdate::UPDATE_ALL);
|
||||
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed);
|
||||
}
|
||||
void Fractorium::OnRandomColorSpeedButtonClicked(bool b) { m_Controller->RandomColorSpeedButtonClicked(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set all xform color speeds to either 0 and 0.5, sequentially toggling.
|
||||
/// Called when the Toggle Color Speed button is clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ToggleColorSpeedButtonClicked()
|
||||
{
|
||||
char ch = 1;
|
||||
UpdateXform([&](Xform<T>* xform) { xform->m_ColorSpeed = (T(ch ^= 1) ? 0.5 : 0.0); }, eXformUpdate::UPDATE_ALL);
|
||||
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed);
|
||||
}
|
||||
void Fractorium::OnToggleColorSpeedButtonClicked(bool b) { m_Controller->ToggleColorSpeedButtonClicked(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the color speed of the selected xforms.
|
||||
/// Called when xform color speed spinner is changed.
|
||||
@ -184,7 +213,7 @@ void Fractorium::OnXformRefPaletteResized(int logicalIndex, int oldSize, int new
|
||||
template <typename T>
|
||||
QColor FractoriumEmberController<T>::ColorIndexToQColor(double d)
|
||||
{
|
||||
v4T entry = m_Ember.m_Palette[Clamp<size_t>(d * COLORMAP_LENGTH_MINUS_1, 0, m_Ember.m_Palette.Size())];
|
||||
v4F entry = m_Ember.m_Palette[Clamp<size_t>(d * COLORMAP_LENGTH_MINUS_1, 0, m_Ember.m_Palette.Size())];
|
||||
entry.r *= 255;
|
||||
entry.g *= 255;
|
||||
entry.b *= 255;
|
||||
@ -227,5 +256,5 @@ void Fractorium::SetPaletteTableItem(QPixmap* pixmap, QTableWidget* table, QTabl
|
||||
template class FractoriumEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FractoriumEmberController<double>;
|
||||
template class FractoriumEmberController<double>;
|
||||
#endif
|
||||
|
@ -35,7 +35,7 @@ template <typename T>
|
||||
GLEmberController<T>::GLEmberController(Fractorium* fractorium, GLWidget* glWidget, FractoriumEmberController<T>* controller)
|
||||
: GLEmberControllerBase(fractorium, glWidget)
|
||||
{
|
||||
GridStep = T(1.0 / 8.0);
|
||||
GridStep = T(1.0 / 4.0); // michel, needs to insert on GUI to be flexible//TODO
|
||||
m_FractoriumEmberController = controller;
|
||||
m_HoverXform = nullptr;
|
||||
m_SelectedXform = nullptr;
|
||||
|
@ -112,9 +112,8 @@ public:
|
||||
void CalcDragXAxis();
|
||||
void CalcDragYAxis();
|
||||
void CalcDragTranslation();
|
||||
|
||||
void SetEmber(Ember<T>* ember);
|
||||
void SetSelectedXform(Xform<T>* xform);
|
||||
void DrawGrid();
|
||||
void DrawAffine(Xform<T>* xform, bool pre, bool selected);
|
||||
int UpdateHover(v3T& glCoords);
|
||||
bool CheckXformHover(Xform<T>* xform, v3T& glCoords, T& bestDist, bool pre, bool post);
|
||||
@ -136,7 +135,6 @@ private:
|
||||
v3T m_MouseWorldPos;
|
||||
v3T m_MouseDownWorldPos;
|
||||
v3T m_DragHandlePos;
|
||||
v3T m_DragHandleOffset;
|
||||
v3T m_HoverHandlePos;
|
||||
|
||||
m4T m_Modelview;
|
||||
|
@ -135,8 +135,8 @@ void GLEmberController<T>::ClearWindow()
|
||||
{
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
m_GL->makeCurrent();
|
||||
m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_GL->glClearColor(ember->m_Background.r, ember->m_Background.g, ember->m_Background.b, 1.0);
|
||||
m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -209,7 +209,17 @@ void GLWidget::paintGL()
|
||||
{
|
||||
auto renderer = controller->Renderer();
|
||||
m_Drawing = true;
|
||||
GLController()->DrawImage();
|
||||
|
||||
if (m_Fractorium->DrawImage())
|
||||
{
|
||||
GLController()->DrawImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
//Affine drawing.
|
||||
bool pre = m_Fractorium->ui.PreAffineGroupBox->isChecked();
|
||||
bool post = m_Fractorium->ui.PostAffineGroupBox->isChecked();
|
||||
@ -249,8 +259,8 @@ void GLEmberController<T>::DrawImage()
|
||||
{
|
||||
auto renderer = m_FractoriumEmberController->Renderer();
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_GL->glClearColor(ember->m_Background.r, ember->m_Background.g, ember->m_Background.b, 1.0);
|
||||
m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_GL->glDisable(GL_DEPTH_TEST);
|
||||
renderer->EnterFinalAccum();//Lock, may not be necessary, but just in case.
|
||||
renderer->EnterResize();
|
||||
@ -276,16 +286,16 @@ void GLEmberController<T>::DrawImage()
|
||||
template <typename T>
|
||||
void GLEmberController<T>::DrawAffines(bool pre, bool post)
|
||||
{
|
||||
if (!m_Fractorium->DrawXforms() || (m_DragState == eDragState::DragRotateScale))
|
||||
return;
|
||||
|
||||
QueryVMP();//Resolves to float or double specialization function depending on T.
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
bool dragging = m_DragState == eDragState::DragDragging;
|
||||
|
||||
//Draw grid if control key is pressed.
|
||||
if (m_GL->hasFocus() && GetControl())
|
||||
{
|
||||
m_GL->glLineWidth(1.0f);
|
||||
m_GL->DrawGrid(m_FractoriumEmberController->AffineScaleLockedToCurrent());
|
||||
}
|
||||
if ((m_GL->hasFocus() && GetControl()) || m_Fractorium->DrawGrid())
|
||||
DrawGrid();
|
||||
|
||||
//When dragging, only draw the selected xform's affine and hide all others.
|
||||
if (!m_Fractorium->m_Settings->ShowAllXforms() && dragging)
|
||||
@ -376,7 +386,7 @@ bool GLEmberControllerBase::KeyPress_(QKeyEvent* e)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call controller KeyPress_().
|
||||
/// Call controller KeyPress().
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void GLWidget::keyPressEvent(QKeyEvent* e)
|
||||
@ -466,15 +476,7 @@ void GLEmberController<T>::MousePress(QMouseEvent* e)
|
||||
m_DragSrcTransforms.push_back(m_AffineType == eAffineType::AffinePre ? xform->m_Affine : xform->m_Post);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Don't update renderer here.
|
||||
m_DragHandlePos = m_HoverHandlePos;//The location in local coordinates of the point selected on the spinner, x, y or center.
|
||||
m_DragHandleOffset = m_DragHandlePos - m_MouseWorldPos;//The distance in world coordinates from the point selected to the center of the spinner.
|
||||
m_DragState = eDragState::DragDragging;
|
||||
//Draw large yellow dot on select or drag.
|
||||
m_GL->glPointSize(6.0f);
|
||||
m_GL->glBegin(GL_POINTS);
|
||||
m_GL->glColor4f(1.0f, 1.0f, 0.5f, 1.0f);
|
||||
m_GL->glVertex2f(m_DragHandlePos.x, m_DragHandlePos.y);
|
||||
m_GL->glEnd();
|
||||
m_GL->glPointSize(1.0f);//Restore point size.
|
||||
m_GL->repaint();
|
||||
}
|
||||
else//Nothing was selected.
|
||||
@ -491,13 +493,16 @@ void GLEmberController<T>::MousePress(QMouseEvent* e)
|
||||
}
|
||||
else if (e->button() == Qt::RightButton)//Right button does whole image rotation and scaling.
|
||||
{
|
||||
UpdateHover(mouseFlipped);
|
||||
m_SelectedXform = m_HoverXform;
|
||||
m_CenterDownX = ember->m_CenterX;//Capture these because they will change when rotating and scaling.
|
||||
m_CenterDownY = ember->m_CenterY;
|
||||
m_RotationDown = ember->m_Rotate;
|
||||
m_ScaleDown = ember->m_PixelsPerUnit;
|
||||
m_DragState = eDragState::DragRotateScale;
|
||||
if (m_Fractorium->DrawImage())
|
||||
{
|
||||
UpdateHover(mouseFlipped);
|
||||
m_SelectedXform = m_HoverXform;
|
||||
m_CenterDownX = ember->m_CenterX;//Capture these because they will change when rotating and scaling.
|
||||
m_CenterDownY = ember->m_CenterY;
|
||||
m_RotationDown = ember->m_Rotate;
|
||||
m_ScaleDown = ember->m_PixelsPerUnit;
|
||||
m_DragState = eDragState::DragRotateScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -535,7 +540,7 @@ void GLEmberController<T>::MouseRelease(QMouseEvent* e)
|
||||
|
||||
m_DragState = eDragState::DragNone;
|
||||
m_DragModifier = 0;
|
||||
m_GL->repaint();//Force immediate redraw.
|
||||
m_GL->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -670,15 +675,27 @@ void GLWidget::mouseMoveEvent(QMouseEvent* e)
|
||||
/// will zoom in the image in our out, while sacrificing quality.
|
||||
/// If the user needs to preserve quality, they can use the zoom spinner
|
||||
/// on the main window.
|
||||
/// If Alt is pressed, only the scale of the affines is changed, the scale of the image remains untouched.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
template <typename T>
|
||||
void GLEmberController<T>::Wheel(QWheelEvent* e)
|
||||
{
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
if ((e->modifiers() & Qt::AltModifier) && m_Fractorium->DrawXforms())
|
||||
{
|
||||
m_FractoriumEmberController->ChangeLockedScale(e->angleDelta().x() >= 0 ? 1.0981 : 0.9);
|
||||
m_GL->update();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Fractorium->DrawImage())
|
||||
{
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
|
||||
if (m_Fractorium && !(e->buttons() & Qt::MiddleButton))//Middle button does whole image translation, so ignore the mouse wheel while panning to avoid inadvertent zooming.
|
||||
m_Fractorium->SetScale(ember->m_PixelsPerUnit + (e->angleDelta().y() >= 0 ? 50 : -50));
|
||||
if (!(e->buttons() & Qt::MiddleButton))//Middle button does whole image translation, so ignore the mouse wheel while panning to avoid inadvertent zooming.
|
||||
m_Fractorium->SetScale(ember->m_PixelsPerUnit + (e->angleDelta().y() >= 0 ? 50 : -50));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -799,76 +816,6 @@ bool GLEmberController<T>::SizesMatch()
|
||||
m_GL->m_TexHeight == m_GL->m_ViewHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the grid in response to the control key being pressed.
|
||||
/// The frequency of the grid lines will change depending on the zoom.
|
||||
/// Calculated with the frame always centered, the renderer just moves the camera.
|
||||
/// </summary>
|
||||
/// <param name="scale">A value to scale by, used when locking the affine scale</param>
|
||||
void GLWidget::DrawGrid(double scale)
|
||||
{
|
||||
auto renderer = m_Fractorium->m_Controller->Renderer();
|
||||
float unitX = std::abs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f;
|
||||
float unitY = std::abs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f;
|
||||
float rad = std::max(unitX * scale, unitY * scale);
|
||||
float xLow = floor(-unitX);
|
||||
float xHigh = ceil(unitX);
|
||||
float yLow = floor(-unitY);
|
||||
float yHigh = ceil(unitY);
|
||||
glBegin(GL_LINES);
|
||||
|
||||
if (rad <= 8.0f)
|
||||
{
|
||||
glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
|
||||
|
||||
for (float fx = xLow; fx <= xHigh; fx += GridStep)
|
||||
{
|
||||
glVertex2f(fx, yLow);
|
||||
glVertex2f(fx, yHigh);
|
||||
}
|
||||
|
||||
for (float fy = yLow; fy < yHigh; fy += GridStep)
|
||||
{
|
||||
glVertex2f(xLow, fy);
|
||||
glVertex2f(xHigh, fy);
|
||||
}
|
||||
}
|
||||
|
||||
unitX *= scale;
|
||||
unitY *= scale;
|
||||
|
||||
if (unitX <= 64.0f)
|
||||
{
|
||||
glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
for (float fx = xLow; fx <= xHigh; fx += 1.0f)
|
||||
{
|
||||
glVertex2f(fx, yLow);
|
||||
glVertex2f(fx, yHigh);
|
||||
}
|
||||
|
||||
for (float fy = yLow; fy < yHigh; fy += 1.0f)
|
||||
{
|
||||
glVertex2f(xLow, fy);
|
||||
glVertex2f(xHigh, fy);
|
||||
}
|
||||
}
|
||||
|
||||
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
glVertex2f(0.0f, 0.0f);
|
||||
glVertex2f(xHigh, 0.0f);
|
||||
glColor4f(0.5f, 0.0f, 0.0f, 1.0f);
|
||||
glVertex2f(0.0f, 0.0f);
|
||||
glVertex2f(xLow, 0.0f);
|
||||
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glVertex2f(0.0f, 0.0f);
|
||||
glVertex2f(0.0f, yHigh);
|
||||
glColor4f(0.0f, 0.5f, 0.0f, 1.0f);
|
||||
glVertex2f(0.0f, 0.0f);
|
||||
glVertex2f(0.0f, yLow);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the unit square.
|
||||
/// </summary>
|
||||
@ -894,6 +841,60 @@ void GLWidget::DrawUnitSquare()
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the grid
|
||||
/// The frequency of the grid lines will change depending on the zoom (ALT+WHEEL).
|
||||
/// Calculated with the frame always centered, the renderer just moves the camera.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void GLEmberController<T>::DrawGrid()
|
||||
{
|
||||
auto renderer = m_Fractorium->m_Controller->Renderer();
|
||||
double scale = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
float unitX = (std::abs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f) / scale;
|
||||
float unitY = (std::abs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f) / scale;
|
||||
float xLow = floor(-unitX);
|
||||
float xHigh = ceil(unitX);
|
||||
float yLow = floor(-unitY);
|
||||
float yHigh = ceil(unitY);
|
||||
float alpha = 0.25f;
|
||||
Affine2D<T> temp;
|
||||
m4T mat = (temp * scale).ToMat4RowMajor();
|
||||
m_GL->glPushMatrix();
|
||||
m_GL->glLoadIdentity();
|
||||
MultMatrix(mat);
|
||||
m_GL->glLineWidth(1.0f);
|
||||
m_GL->glBegin(GL_LINES);
|
||||
m_GL->glColor4f(0.5f, 0.5f, 0.5f, alpha);
|
||||
|
||||
for (float fx = xLow; fx <= xHigh; fx += GridStep)
|
||||
{
|
||||
m_GL->glVertex2f(fx, yLow);
|
||||
m_GL->glVertex2f(fx, yHigh);
|
||||
}
|
||||
|
||||
for (float fy = yLow; fy < yHigh; fy += GridStep)
|
||||
{
|
||||
m_GL->glVertex2f(xLow, fy);
|
||||
m_GL->glVertex2f(xHigh, fy);
|
||||
}
|
||||
|
||||
m_GL->glColor4f(1.0f, 0.0f, 0.0f, alpha);
|
||||
m_GL->glVertex2f(0.0f, 0.0f);
|
||||
m_GL->glVertex2f(xHigh, 0.0f);
|
||||
m_GL->glColor4f(0.5f, 0.0f, 0.0f, alpha);
|
||||
m_GL->glVertex2f(0.0f, 0.0f);
|
||||
m_GL->glVertex2f(xLow, 0.0f);
|
||||
m_GL->glColor4f(0.0f, 1.0f, 0.0f, alpha);
|
||||
m_GL->glVertex2f(0.0f, 0.0f);
|
||||
m_GL->glVertex2f(0.0f, yHigh);
|
||||
m_GL->glColor4f(0.0f, 0.5f, 0.0f, alpha);
|
||||
m_GL->glVertex2f(0.0f, 0.0f);
|
||||
m_GL->glVertex2f(0.0f, yLow);
|
||||
m_GL->glEnd();
|
||||
m_GL->glPopMatrix();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the pre or post affine circle for the passed in xform.
|
||||
/// For drawing affine transforms, multiply the identity model view matrix by the
|
||||
@ -1028,42 +1029,45 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
m_HoverType = eHoverType::HoverNone;
|
||||
|
||||
//If there's a selected/current xform, check it first so it gets precedence over the others.
|
||||
if (m_SelectedXform)
|
||||
if (m_Fractorium->DrawXforms())//Don't bother checking anything if the user wants to see no xforms.
|
||||
{
|
||||
//These checks prevent highlighting the pre/post selected xform circle, when one is set to show all, and the other
|
||||
//is set to show current, and the user hovers over another xform, but doesn't select it, then moves the mouse
|
||||
//back over the hidden circle for the pre/post that was set to only show current.
|
||||
bool checkSelPre = preAll || (pre && m_HoverXform == m_SelectedXform);
|
||||
bool checkSelPost = postAll || (post && m_HoverXform == m_SelectedXform);
|
||||
|
||||
if (CheckXformHover(m_SelectedXform, glCoords, bestDist, checkSelPre, checkSelPost))
|
||||
//If there's a selected/current xform, check it first so it gets precedence over the others.
|
||||
if (m_SelectedXform)
|
||||
{
|
||||
m_HoverXform = m_SelectedXform;
|
||||
bestIndex = int(ember->GetTotalXformIndex(m_SelectedXform));
|
||||
}
|
||||
}
|
||||
//These checks prevent highlighting the pre/post selected xform circle, when one is set to show all, and the other
|
||||
//is set to show current, and the user hovers over another xform, but doesn't select it, then moves the mouse
|
||||
//back over the hidden circle for the pre/post that was set to only show current.
|
||||
bool checkSelPre = preAll || (pre && m_HoverXform == m_SelectedXform);
|
||||
bool checkSelPost = postAll || (post && m_HoverXform == m_SelectedXform);
|
||||
|
||||
//Check all xforms.
|
||||
for (int i = 0; i < int(ember->TotalXformCount()); i++)
|
||||
{
|
||||
auto xform = ember->GetTotalXform(i);
|
||||
|
||||
if (preAll || (pre && m_HoverXform == xform))//Only check pre affine if they are shown.
|
||||
{
|
||||
if (CheckXformHover(xform, glCoords, bestDist, true, false))
|
||||
if (CheckXformHover(m_SelectedXform, glCoords, bestDist, checkSelPre, checkSelPost))
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
m_HoverXform = m_SelectedXform;
|
||||
bestIndex = int(ember->GetTotalXformIndex(m_SelectedXform));
|
||||
}
|
||||
}
|
||||
|
||||
if (postAll || (post && m_HoverXform == xform))//Only check post affine if they are shown.
|
||||
//Check all xforms.
|
||||
for (int i = 0; i < int(ember->TotalXformCount()); i++)
|
||||
{
|
||||
if (CheckXformHover(xform, glCoords, bestDist, false, true))
|
||||
auto xform = ember->GetTotalXform(i);
|
||||
|
||||
if (preAll || (pre && m_HoverXform == xform))//Only check pre affine if they are shown.
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
if (CheckXformHover(xform, glCoords, bestDist, true, false))
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (postAll || (post && m_HoverXform == xform))//Only check post affine if they are shown.
|
||||
{
|
||||
if (CheckXformHover(xform, glCoords, bestDist, false, true))
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1207,18 +1211,17 @@ template <typename T>
|
||||
void GLEmberController<T>::CalcDragXAxis()
|
||||
{
|
||||
size_t index = 0;
|
||||
auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
|
||||
auto scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
auto affineToWorldScale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
|
||||
auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
bool pre = m_AffineType == eAffineType::AffinePre;
|
||||
bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt();
|
||||
auto startDiff = (v2T(m_HoverHandlePos) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T startAngle = std::atan2(startDiff.y, startDiff.x);
|
||||
|
||||
if (GetShift())
|
||||
{
|
||||
auto posOffset = m_MouseWorldPos + m_DragHandleOffset;
|
||||
v3T snapped = GetControl() ? SnapToNormalizedAngle(posOffset, 24u) : posOffset;
|
||||
auto startDiff = (v2T(m_MouseDownWorldPos) * scale) - m_DragSrcTransform.O();
|
||||
auto endDiff = (v2T(snapped) * scale) - m_DragSrcTransform.O();
|
||||
T startAngle = std::atan2(startDiff.y, startDiff.x);
|
||||
v3T snapped = GetControl() ? SnapToNormalizedAngle(m_MouseWorldPos, 24u) : m_MouseWorldPos;
|
||||
auto endDiff = (v2T(snapped) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
|
||||
@ -1245,32 +1248,47 @@ void GLEmberController<T>::CalcDragXAxis()
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.X()) * scaleBack, 0);
|
||||
m_DragHandlePos = v3T((affine.O() + affine.X()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Calling code will update renderer.
|
||||
}
|
||||
else
|
||||
{
|
||||
v3T diff;
|
||||
auto posOffset = m_MouseWorldPos + m_DragHandleOffset;
|
||||
v3T diff = m_MouseWorldPos - m_MouseDownWorldPos;
|
||||
auto diffscale = diff * affineToWorldScale;
|
||||
auto origmag = Zeps(glm::length(m_DragSrcTransform.X()));
|
||||
auto origXPlusOff = v3T(m_DragSrcTransform.X(), 0) + diffscale;
|
||||
|
||||
if (GetControl())
|
||||
diff = SnapToGrid(posOffset) - m_MouseDownWorldPos;
|
||||
else
|
||||
diff = posOffset - m_MouseDownWorldPos;
|
||||
{
|
||||
auto o3 = v3T(m_DragSrcTransform.O(), 0);
|
||||
auto o3x = origXPlusOff + o3;
|
||||
origXPlusOff = SnapToGrid(o3x);
|
||||
origXPlusOff -= o3;
|
||||
}
|
||||
|
||||
auto origXPlusOff = v3T(m_DragSrcTransform.X(), 0) + (diff * scale);
|
||||
auto newmag = glm::length(origXPlusOff);
|
||||
auto newprc = newmag / origmag;
|
||||
auto endDiff = (v2T(origXPlusOff) * affineToWorldScale);
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
auto axis = v3T(m_DragSrcTransforms[index++].X(), 0) + (diff * scale);
|
||||
auto src = m_DragSrcTransforms[index++];
|
||||
|
||||
if (GetAlt())
|
||||
{
|
||||
affine.X(v2T(origXPlusOff));//Absolute, not ratio.
|
||||
}
|
||||
else
|
||||
affine.RotateScaleXTo(v2T(axis));
|
||||
{
|
||||
src.ScaleXY(newprc);
|
||||
src.Rotate(angle);
|
||||
affine = src;
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.X()) * scaleBack, 0);
|
||||
m_DragHandlePos = v3T((affine.O() + affine.X()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);
|
||||
}
|
||||
}
|
||||
@ -1297,18 +1315,17 @@ template <typename T>
|
||||
void GLEmberController<T>::CalcDragYAxis()
|
||||
{
|
||||
size_t index = 0;
|
||||
auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
|
||||
auto scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
auto affineToWorldScale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
|
||||
auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
bool pre = m_AffineType == eAffineType::AffinePre;
|
||||
bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt();
|
||||
auto startDiff = (v2T(m_HoverHandlePos) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T startAngle = std::atan2(startDiff.y, startDiff.x);
|
||||
|
||||
if (GetShift())
|
||||
{
|
||||
auto posOffset = m_MouseWorldPos + m_DragHandleOffset;
|
||||
v3T snapped = GetControl() ? SnapToNormalizedAngle(posOffset, 24u) : posOffset;
|
||||
auto startDiff = (v2T(m_MouseDownWorldPos) * scale) - m_DragSrcTransform.O();
|
||||
auto endDiff = (v2T(snapped) * scale) - m_DragSrcTransform.O();
|
||||
T startAngle = std::atan2(startDiff.y, startDiff.x);
|
||||
v3T snapped = GetControl() ? SnapToNormalizedAngle(m_MouseWorldPos, 24u) : m_MouseWorldPos;
|
||||
auto endDiff = (v2T(snapped) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
|
||||
@ -1335,32 +1352,47 @@ void GLEmberController<T>::CalcDragYAxis()
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.Y()) * scaleBack, 0);
|
||||
m_DragHandlePos = v3T((affine.O() + affine.Y()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Calling code will update renderer.
|
||||
}
|
||||
else
|
||||
{
|
||||
v3T diff;
|
||||
auto posOffset = m_MouseWorldPos + m_DragHandleOffset;
|
||||
v3T diff = m_MouseWorldPos - m_MouseDownWorldPos;
|
||||
auto diffscale = diff * affineToWorldScale;
|
||||
auto origmag = Zeps(glm::length(m_DragSrcTransform.Y()));
|
||||
auto origYPlusOff = v3T(m_DragSrcTransform.Y(), 0) + diffscale;
|
||||
|
||||
if (GetControl())
|
||||
diff = SnapToGrid(posOffset) - m_MouseDownWorldPos;
|
||||
else
|
||||
diff = posOffset - m_MouseDownWorldPos;
|
||||
{
|
||||
auto o3 = v3T(m_DragSrcTransform.O(), 0);
|
||||
auto o3y = origYPlusOff + o3;
|
||||
origYPlusOff = SnapToGrid(o3y);
|
||||
origYPlusOff -= o3;
|
||||
}
|
||||
|
||||
auto origXPlusOff = v3T(m_DragSrcTransform.Y(), 0) + (diff * scale);
|
||||
auto newmag = glm::length(origYPlusOff);
|
||||
auto newprc = newmag / origmag;
|
||||
auto endDiff = (v2T(origYPlusOff) * affineToWorldScale);
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
auto axis = v3T(m_DragSrcTransforms[index++].Y(), 0) + (diff * scale);
|
||||
auto src = m_DragSrcTransforms[index++];
|
||||
|
||||
if (GetAlt())
|
||||
affine.Y(v2T(origXPlusOff));//Absolute, not ratio.
|
||||
{
|
||||
affine.Y(v2T(origYPlusOff));//Absolute, not ratio.
|
||||
}
|
||||
else
|
||||
affine.RotateScaleYTo(v2T(axis));
|
||||
{
|
||||
src.ScaleXY(newprc);
|
||||
src.Rotate(angle);
|
||||
affine = src;
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.Y()) * scaleBack, 0);
|
||||
m_DragHandlePos = v3T((affine.O() + affine.Y()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);
|
||||
}
|
||||
}
|
||||
@ -1382,8 +1414,8 @@ template <typename T>
|
||||
void GLEmberController<T>::CalcDragTranslation()
|
||||
{
|
||||
size_t index = 0;
|
||||
auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
|
||||
auto scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
auto affineToWorldScale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
|
||||
auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
bool worldPivotShift = !m_Fractorium->LocalPivot() && GetShift();
|
||||
bool pre = m_AffineType == eAffineType::AffinePre;
|
||||
|
||||
@ -1409,7 +1441,7 @@ void GLEmberController<T>::CalcDragTranslation()
|
||||
affine.O(srcRotated.O());
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T(srcRotated.O(), 0) * scaleBack;
|
||||
m_DragHandlePos = v3T(srcRotated.O(), 0) * worldToAffineScale;
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Calling code will update renderer.
|
||||
}
|
||||
else
|
||||
@ -1421,20 +1453,24 @@ void GLEmberController<T>::CalcDragTranslation()
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
auto offset = m_DragSrcTransforms[index++].O() + (scale * v2T(diff));
|
||||
auto offset = m_DragSrcTransforms[index++].O() + (affineToWorldScale * v2T(diff));
|
||||
auto snapped = SnapToGrid(offset);
|
||||
affine.O(v2T(snapped.x, snapped.y));
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T(affine.O(), 0) * worldToAffineScale;
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);
|
||||
m_DragHandlePos = SnapToGrid(m_MouseWorldPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
affine.O(m_DragSrcTransforms[index++].O() + (scale * v2T(diff)));
|
||||
affine.O(m_DragSrcTransforms[index++].O() + (affineToWorldScale * v2T(diff)));
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T(affine.O(), 0) * worldToAffineScale;
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);
|
||||
m_DragHandlePos = m_MouseWorldPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1454,5 +1490,5 @@ GLEmberControllerBase* GLWidget::GLController()
|
||||
template class GLEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class GLEmberController<double>;
|
||||
template class GLEmberController<double>;
|
||||
#endif
|
||||
|
@ -11,8 +11,6 @@ class GLEmberControllerBase;
|
||||
template<typename T> class GLEmberController;
|
||||
template<typename T> class FractoriumEmberController;
|
||||
|
||||
static const float GridStep = 1.0f / 8.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The main drawing area.
|
||||
/// This uses the Qt wrapper around OpenGL to draw the output of the render to a texture whose
|
||||
@ -68,7 +66,6 @@ private:
|
||||
bool Allocate(bool force = false);
|
||||
bool Deallocate();
|
||||
void SetViewport();
|
||||
void DrawGrid(double scale);
|
||||
void DrawUnitSquare();
|
||||
void DrawAffineHelper(int index, bool selected, bool pre, bool final, bool background);
|
||||
GLEmberControllerBase* GLController();
|
||||
|
@ -10,6 +10,7 @@
|
||||
/// <returns>0 if successful, else 1.</returns>
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int rv = -1;
|
||||
QApplication a(argc, argv);
|
||||
#ifdef TEST_CL
|
||||
QMessageBox::critical(QApplication::desktop(), "Error", "Fractorium cannot be run in test mode, undefine TEST_CL first.");
|
||||
@ -19,16 +20,9 @@ int main(int argc, char* argv[])
|
||||
QMessageBox::critical(QApplication::desktop(), "Error", "Fractorium cannot be run in test mode, undefine ISAAC_FLAM3_DEBUG first.");
|
||||
return 1;
|
||||
#endif
|
||||
//Required for large allocs, else GPU memory usage will be severely limited to small sizes.
|
||||
//This must be done in the application and not in the EmberCL DLL.
|
||||
#ifdef _WIN32
|
||||
_putenv_s("GPU_MAX_ALLOC_PERCENT", "100");
|
||||
#else
|
||||
putenv(const_cast<char*>("GPU_MAX_ALLOC_PERCENT=100"));
|
||||
#endif
|
||||
int rv = -1;
|
||||
auto vf = VarFuncs<float>::Instance();//Create instances that will stay alive until the program exits.
|
||||
auto vlf = VariationList<float>::Instance();
|
||||
auto settings = FractoriumSettings::Instance();
|
||||
#ifdef DO_DOUBLE
|
||||
auto vd = VarFuncs<float>::Instance();
|
||||
auto vld = VariationList<double>::Instance();//No further creations should occur after this.
|
||||
@ -36,6 +30,13 @@ int main(int argc, char* argv[])
|
||||
|
||||
try
|
||||
{
|
||||
//Required for large allocs, else GPU memory usage will be severely limited to small sizes.
|
||||
//This must be done in the application and not in the EmberCL DLL.
|
||||
#ifdef _WIN32
|
||||
_putenv_s("GPU_MAX_ALLOC_PERCENT", "100");
|
||||
#else
|
||||
putenv(const_cast<char*>("GPU_MAX_ALLOC_PERCENT=100"));
|
||||
#endif
|
||||
Fractorium w;
|
||||
w.show();
|
||||
a.installEventFilter(&w);
|
||||
@ -43,11 +44,11 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
QMessageBox::critical(0, "Fatal Error", QString::fromStdString(e.what()));
|
||||
QMessageBox::critical(nullptr, "Fatal Error", QString::fromStdString(e.what()));
|
||||
}
|
||||
catch (const char* e)
|
||||
{
|
||||
QMessageBox::critical(0, "Fatal Error", e);
|
||||
QMessageBox::critical(nullptr, "Fatal Error", e);
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
357
Source/Fractorium/PaletteEditor.ui
Normal file
357
Source/Fractorium/PaletteEditor.ui
Normal file
@ -0,0 +1,357 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PaletteEditor</class>
|
||||
<widget class="QDialog" name="PaletteEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>942</width>
|
||||
<height>542</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Palette Editor</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="LeftSideVertialLayout">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="LeftTopHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="NewPaletteFileButton">
|
||||
<property name="text">
|
||||
<string>New Palette File...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="CopyPaletteFileButton">
|
||||
<property name="text">
|
||||
<string>Copy Palette File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="PaletteFilenameCombo"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="PaletteListTable">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="autoScroll">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::AllEditTriggers</set>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectItems</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>16</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>16</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Palette</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="LeftBottomHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="AppendPaletteButton">
|
||||
<property name="text">
|
||||
<string>Append Palette</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="OverwritePaletteButton">
|
||||
<property name="text">
|
||||
<string>Overwrite Palette</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="DeletePaletteButton">
|
||||
<property name="text">
|
||||
<string>Delete Palette</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="MainVerticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="ColorViewGroupBox">
|
||||
<property name="title">
|
||||
<string>Palette</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="ColorPickerGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Color Picker</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="ControlsGroupBox">
|
||||
<property name="title">
|
||||
<string>Adjustment Controls</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,4">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</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>
|
||||
<property name="text">
|
||||
<string>Sync</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QSpinBox" name="ArrowsSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="buttonSymbols">
|
||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>Arrows: </string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="AutoDistributeCheckBox">
|
||||
<property name="text">
|
||||
<string>Auto Distribute</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="AdjustmentRightGridLayout">
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="CreatePaletteFromImageButton">
|
||||
<property name="text">
|
||||
<string>Create From Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="ResetColorsButton">
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QPushButton" name="RandomColorsButton">
|
||||
<property name="text">
|
||||
<string>Random Colors</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="RemoveColorButton">
|
||||
<property name="text">
|
||||
<string>Remove Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="DistributeColorsButton">
|
||||
<property name="text">
|
||||
<string>Distribute Colors</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="AddColorButton">
|
||||
<property name="text">
|
||||
<string>Add Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="InvertColorsButton">
|
||||
<property name="text">
|
||||
<string>Invert Colors</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QPushButton" name="CreatePaletteAgainFromImageButton">
|
||||
<property name="text">
|
||||
<string>Create Again From Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>PaletteEditor</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PaletteEditor</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
79
Source/Fractorium/PaletteEditor/ColorPanel.cpp
Normal file
79
Source/Fractorium/PaletteEditor/ColorPanel.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorPanel.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and initializes the minimum size.
|
||||
/// </summary>
|
||||
/// <param name="p">The parent widget</param>
|
||||
ColorPanel::ColorPanel(QWidget* p)
|
||||
: QPushButton(p)
|
||||
{
|
||||
setMinimumSize(10, 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derived paint event which paints the entire button surface with m_Color.
|
||||
/// </summary>
|
||||
void ColorPanel::paintEvent(QPaintEvent*)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.setPen(m_Pen);
|
||||
p.setBrush(QBrush(m_Color));
|
||||
p.drawRect(QRect(2, 2, width() - 6, height() - 6));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the pen object used to paint the button.
|
||||
/// </summary>
|
||||
/// <param name="pen">The pen object used to paint the button</param>
|
||||
void ColorPanel::Pen(const QPen& pen)
|
||||
{
|
||||
m_Pen = pen;
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the pen object used to paint the button.
|
||||
/// </summary>
|
||||
/// <returns>QPen</returns>
|
||||
QPen ColorPanel::Pen() const
|
||||
{
|
||||
return m_Pen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the color used to paint the button.
|
||||
/// </summary>
|
||||
/// <param name="color">The color used to paint the button</param>
|
||||
void ColorPanel::Color(const QColor& color)
|
||||
{
|
||||
m_Color = color;
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the color used to paint the button.
|
||||
/// </summary>
|
||||
/// <returns>QColor</returns>
|
||||
QColor ColorPanel::Color() const
|
||||
{
|
||||
return m_Color;
|
||||
}
|
47
Source/Fractorium/PaletteEditor/ColorPanel.h
Normal file
47
Source/Fractorium/PaletteEditor/ColorPanel.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// A derivation of a QPushButton which makes a large, clickable panel
|
||||
/// which custom paints the button based on a user specified color.
|
||||
/// </summary>
|
||||
class ColorPanel : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorPanel(QWidget* p = nullptr);
|
||||
|
||||
void Pen(const QPen& pen);
|
||||
QPen Pen() const;
|
||||
void Color(const QColor& color);
|
||||
QColor Color() const;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
QPen m_Pen;
|
||||
QColor m_Color;
|
||||
};
|
96
Source/Fractorium/PaletteEditor/ColorPickerWidget.cpp
Normal file
96
Source/Fractorium/PaletteEditor/ColorPickerWidget.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorPickerWidget.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and initializes the member controls
|
||||
/// on a grid layout.
|
||||
/// </summary>
|
||||
/// <param name="p">The parent widget</param>
|
||||
ColorPickerWidget::ColorPickerWidget(QWidget* p)
|
||||
: QWidget(p)
|
||||
{
|
||||
m_ColorTriangle = new ColorTriangle(this);
|
||||
m_ColorPanel = new ColorPanel(this);
|
||||
m_ColorDialog = new QColorDialog(this);
|
||||
m_ColorPanel->Color(m_ColorTriangle->Color());
|
||||
connect(m_ColorTriangle, SIGNAL(ColorChanged(const QColor&)), this, SLOT(OnTriangleColorChanged(const QColor&)));
|
||||
connect(m_ColorPanel, SIGNAL(clicked()), this, SLOT(OnColorViewerClicked()));
|
||||
auto layout = new QGridLayout(this);
|
||||
layout->setMargin(4);
|
||||
layout->addWidget(m_ColorTriangle, 0, 0, 3, 1);
|
||||
layout->addWidget(m_ColorPanel, 0, 1, 3, 1);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the color used to paint the color panel.
|
||||
/// </summary>
|
||||
/// <returns>QColor</returns>
|
||||
QColor ColorPickerWidget::Color() const
|
||||
{
|
||||
return m_ColorPanel->Color();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the current color for the triangle.
|
||||
/// </summary>
|
||||
/// <param name="col">The parent widget</param>
|
||||
void ColorPickerWidget::SetColorPanelColor(const QColor& col)
|
||||
{
|
||||
if (col.isValid())
|
||||
m_ColorTriangle->Color(col);//Internally emits ColorChanged() which will call OnTriangleColorChanged(), which will call m_ColorPanel->Color().
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden resize event to set the color panel height slightly
|
||||
/// smaller than the container it's in.
|
||||
/// </summary>
|
||||
/// <param name="col">The resize event</param>
|
||||
void ColorPickerWidget::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
m_ColorPanel->setMinimumHeight(event->size().height() - 22);
|
||||
m_ColorPanel->setMaximumHeight(event->size().height() - 22);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot called when the color panel is clicked, which will show the color
|
||||
/// picker dialog.
|
||||
/// </summary>
|
||||
void ColorPickerWidget::OnColorViewerClicked()
|
||||
{
|
||||
m_ColorDialog->setCurrentColor(m_ColorPanel->Color());
|
||||
auto newColor = m_ColorDialog->getColor(m_ColorPanel->Color(), this);
|
||||
SetColorPanelColor(newColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot called when the color on the triangle changes for any reason,
|
||||
/// either user initiated or programatically called.
|
||||
/// </summary>
|
||||
/// <param name="col">The new color on the triangle</param>
|
||||
void ColorPickerWidget::OnTriangleColorChanged(const QColor& col)
|
||||
{
|
||||
if (col.isValid())
|
||||
m_ColorPanel->Color(col);
|
||||
|
||||
emit ColorChanged(col);
|
||||
}
|
54
Source/Fractorium/PaletteEditor/ColorPickerWidget.h
Normal file
54
Source/Fractorium/PaletteEditor/ColorPickerWidget.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorTriangle.h"
|
||||
#include "ColorPanel.h"
|
||||
|
||||
/// <summary>
|
||||
/// Aggregator class to package a color triangle, color panel, and color dialog
|
||||
/// all together on a layout.
|
||||
/// </summary>
|
||||
class ColorPickerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorPickerWidget(QWidget* p = nullptr);
|
||||
|
||||
QColor Color() const;
|
||||
void SetColorPanelColor(const QColor& col);
|
||||
|
||||
Q_SIGNALS:
|
||||
void ColorChanged(const QColor& col);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void OnColorViewerClicked();
|
||||
void OnTriangleColorChanged(const QColor& col);
|
||||
|
||||
private:
|
||||
ColorTriangle* m_ColorTriangle;
|
||||
ColorPanel* m_ColorPanel;
|
||||
QColorDialog* m_ColorDialog;
|
||||
};
|
1600
Source/Fractorium/PaletteEditor/ColorTriangle.cpp
Normal file
1600
Source/Fractorium/PaletteEditor/ColorTriangle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
138
Source/Fractorium/PaletteEditor/ColorTriangle.h
Normal file
138
Source/Fractorium/PaletteEditor/ColorTriangle.h
Normal file
@ -0,0 +1,138 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** This file is part of a Qt Solutions component.
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Solutions Commercial License Agreement provided
|
||||
** with the Software or, alternatively, in accordance with the terms
|
||||
** contained in a written agreement between you and Nokia.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain
|
||||
** additional rights. These rights are described in the Nokia Qt LGPL
|
||||
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
||||
** package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Please note Third Party Software included with Qt Solutions may impose
|
||||
** additional restrictions and it is the user's responsibility to ensure
|
||||
** that they have met the licensing requirements of the GPL, LGPL, or Qt
|
||||
** Solutions Commercial license and the relevant license of the Third
|
||||
** Party Software they are using.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at qt-sales@nokia.com.
|
||||
**
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// DoubleColor, Vertex and ColorTriangle classes.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Used to store color values in the range 0..255 as doubles.
|
||||
/// </summary>
|
||||
struct DoubleColor
|
||||
{
|
||||
double r, g, b;
|
||||
|
||||
DoubleColor() : r(0.0), g(0.0), b(0.0) {}
|
||||
DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
|
||||
DoubleColor(const DoubleColor& c) : r(c.r), g(c.g), b(c.b) {}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Used to store pairs of DoubleColor and DoublePoint in one structure.
|
||||
/// </summary>
|
||||
struct Vertex
|
||||
{
|
||||
DoubleColor color;
|
||||
QPointF point;
|
||||
|
||||
Vertex(const DoubleColor& c, const QPointF& p) : color(c), point(p) {}
|
||||
Vertex(const QColor& c, const QPointF& p)
|
||||
: color(DoubleColor((double)c.red(), (double)c.green(),
|
||||
(double)c.blue())), point(p) {}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Widget for drawing a color triangle which allows users to select colors
|
||||
/// in a manner more intuitive than the usual color picker dialog.
|
||||
/// This class was taken from an open source project named Chaos Helper, which took
|
||||
/// it from the Qt examples, so it mostly remains as-is.
|
||||
/// </summary>
|
||||
class ColorTriangle : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorTriangle(QWidget* parent = nullptr);
|
||||
|
||||
void Polish();
|
||||
QColor Color() const;
|
||||
void Color(const QColor& col);
|
||||
|
||||
virtual int heightForWidth(int w) const override;
|
||||
virtual QSize sizeHint() const override;
|
||||
|
||||
signals:
|
||||
void ColorChanged(const QColor& col);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent*) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent*) override;
|
||||
virtual void mousePressEvent(QMouseEvent*) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent*) override;
|
||||
virtual void keyPressEvent(QKeyEvent* e) override;
|
||||
virtual void resizeEvent(QResizeEvent*) override;
|
||||
|
||||
private:
|
||||
void GenBackground();
|
||||
void DrawTrigon(QImage* p, const QPointF& a, const QPointF& b, const QPointF& c, const QColor& color);
|
||||
double CalcOuterRadius() const;
|
||||
double RadiusAt(const QPointF& pos, const QRect& rect) const;
|
||||
double AngleAt(const QPointF& pos, const QRect& rect) const;
|
||||
QColor ColorFromPoint(const QPointF& p) const;
|
||||
QPointF PointFromColor(const QColor& col) const;
|
||||
QPointF MovePointToTriangle(double x, double y, const Vertex& a, const Vertex& b, const Vertex& c) const;
|
||||
|
||||
bool mustGenerateBackground;
|
||||
int curHue;
|
||||
int penWidth;
|
||||
int ellipseSize;
|
||||
int outerRadius;
|
||||
double a, b, c;
|
||||
QImage bg;
|
||||
QColor curColor;
|
||||
QPointF pa, pb, pc, pd;
|
||||
QPointF selectorPos;
|
||||
|
||||
enum SelectionMode
|
||||
{
|
||||
Idle,
|
||||
SelectingHue,
|
||||
SelectingSatValue
|
||||
} selMode;
|
||||
};
|
78
Source/Fractorium/PaletteEditor/GradientArrow.h
Normal file
78
Source/Fractorium/PaletteEditor/GradientArrow.h
Normal file
@ -0,0 +1,78 @@
|
||||
/****************************************************************************/
|
||||
// This file is part of the gradLib library originally made by Stian Broen
|
||||
//
|
||||
// For more free libraries, please visit <http://broentech.no>
|
||||
//
|
||||
// gradLib is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library. If not, see <http://www.gnu.org/licenses/>
|
||||
/****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// Class for drawing the small arrows below the gradient in the palette editor.
|
||||
/// The drawing is accomplished via a QPolygon object.
|
||||
/// </summary>
|
||||
class GradientArrow
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Default constructor which sets up the size of the arrow.
|
||||
/// </summary>
|
||||
explicit GradientArrow()
|
||||
{
|
||||
QPolygon area;
|
||||
area << QPoint(5, 5) << QPoint(10, 0) << QPoint(15, 5) << QPoint(15, 15) << QPoint(5, 15) << QPoint(5, 5);
|
||||
Area(area);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which takes the color and focus state of the arrow.
|
||||
/// </summary>
|
||||
/// <param name="col">The color of the arrow</param>
|
||||
/// <param name="focus">Whether the arrow is focused</param>
|
||||
explicit GradientArrow(QColor col, bool focus)
|
||||
: GradientArrow()
|
||||
{
|
||||
m_Color = col;
|
||||
m_Focus = focus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy another GradientArrow.
|
||||
/// </summary>
|
||||
/// <param name="other">The GradientArrow object to copy</param>
|
||||
GradientArrow(const GradientArrow& other)
|
||||
: m_Focus(other.Focus()),
|
||||
m_Area(other.Area()),
|
||||
m_Color(other.Color())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getters and setters.
|
||||
/// </summary>
|
||||
inline bool Focus() const { return m_Focus; }
|
||||
inline void Focus(bool val) { m_Focus = val; }
|
||||
inline const QPolygon Area() const { return m_Area; }
|
||||
inline void Area(const QPolygon& val) {m_Area = val; }
|
||||
inline const QColor Color() const { return m_Color; }
|
||||
inline void Color(const QColor& val) { m_Color = val; }
|
||||
|
||||
private:
|
||||
bool m_Focus;
|
||||
QPolygon m_Area;
|
||||
QColor m_Color;
|
||||
};
|
630
Source/Fractorium/PaletteEditor/GradientColorsView.cpp
Normal file
630
Source/Fractorium/PaletteEditor/GradientColorsView.cpp
Normal file
@ -0,0 +1,630 @@
|
||||
/****************************************************************************/
|
||||
// This file is part of the gradLib library originally made by Stian Broen
|
||||
//
|
||||
// For more free libraries, please visit <http://broentech.no>
|
||||
//
|
||||
// gradLib is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library. If not, see <http://www.gnu.org/licenses/>
|
||||
/****************************************************************************/
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "GradientColorsView.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and sets various size constraints.
|
||||
/// </summary>
|
||||
/// <param name="p">The parent widget</param>
|
||||
GradientColorsView::GradientColorsView(QWidget* p)
|
||||
: QWidget(p)
|
||||
{
|
||||
m_ViewRect = QRect(QPoint(0, 0), QPoint(0, 0));
|
||||
qRegisterMetaType<GradientArrow>("GradientArrow");
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMinimumSize(p->width() - 10, p->height() - 10);
|
||||
setMouseTracking(true);
|
||||
ResetToDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the focus to the arrow at the given normalized position.
|
||||
/// </summary>
|
||||
/// <param name="position">The normalized position of the arrow to focus</param>
|
||||
void GradientColorsView::SetFocus(float position)
|
||||
{
|
||||
bool focused = false;
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
focused |= position == it.first;
|
||||
it.second.Focus(position == it.first);
|
||||
}
|
||||
|
||||
if (!focused)
|
||||
m_Arrows.begin()->second.Focus(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the focus to the arrow at the given index.
|
||||
/// </summary>
|
||||
/// <param name="position">The index of the arrow to focus</param>
|
||||
void GradientColorsView::SetFocus(size_t position)
|
||||
{
|
||||
bool focused = false;
|
||||
size_t index = 0;
|
||||
position = std::min(m_Arrows.size() - 1, position);
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
bool b = position == index++;
|
||||
focused |= b;
|
||||
it.second.Focus(b);
|
||||
}
|
||||
|
||||
if (!focused)
|
||||
m_Arrows.begin()->second.Focus(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the color of the currently focused arrow to the passed in color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to set the focused arrow to</param>
|
||||
void GradientColorsView::SetFocusColor(const QColor& color)
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& anArrow = it.second;
|
||||
|
||||
if (anArrow.Focus())
|
||||
{
|
||||
anArrow.Color(color);
|
||||
update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an arrow whose color will be assigned the passed in color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to assign to the new arrow</param>
|
||||
void GradientColorsView::AddArrow(const QColor& color)
|
||||
{
|
||||
float position = 0.5f;
|
||||
|
||||
if (m_Arrows.size() >= 256)
|
||||
return;
|
||||
|
||||
if (m_Arrows.empty())
|
||||
{
|
||||
position = 0;
|
||||
}
|
||||
else if (m_Arrows.size() == 1)
|
||||
{
|
||||
position = (m_Arrows.begin()->first < 1) ? 1 : 0;
|
||||
}
|
||||
else if (m_Arrows.size() == 2)
|
||||
{
|
||||
auto b = m_Arrows.begin();
|
||||
auto rb = m_Arrows.rbegin();
|
||||
position = std::abs((rb->first + b->first) / 2.0);
|
||||
|
||||
if (position == b->first)
|
||||
position = b->first / 2.0;
|
||||
else if (position == rb->first)
|
||||
position = (1.0 + rb->first) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool set = false;
|
||||
auto it = m_Arrows.begin();
|
||||
auto oneBeforeLast = Advance(m_Arrows.begin(), m_Arrows.size() - 1);
|
||||
|
||||
for (; it != oneBeforeLast; ++it)
|
||||
{
|
||||
if (it->second.Focus())
|
||||
{
|
||||
auto next = Advance(it, 1);
|
||||
position = std::abs((next->first + it->first) / 2.0);
|
||||
set = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!set)
|
||||
{
|
||||
it = m_Arrows.begin();
|
||||
position = std::abs((Advance(it, 1)->first + it->first) / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
AddArrow(position, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an arrow whose position and color will be assigned the values passed in.
|
||||
/// If an arrow exists at the specified position, it is overwritten.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to place the new arrow in</param>
|
||||
/// <param name="color">The color to assign to the new arrow</param>
|
||||
void GradientColorsView::AddArrow(float position, const QColor& color)
|
||||
{
|
||||
GradientArrow arrow;
|
||||
arrow.Focus(true);
|
||||
arrow.Color(color);
|
||||
m_Arrows[position] = arrow;
|
||||
SetFocus(position);
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the currently focused arrow if there are more than 2 arrows.
|
||||
/// Set the focus to the arrow whose index is one greater than the one deleted.
|
||||
/// </summary>
|
||||
void GradientColorsView::DeleteFocusedArrow()
|
||||
{
|
||||
if (m_Arrows.size() <= 2)
|
||||
return;
|
||||
|
||||
size_t index = 0;
|
||||
|
||||
for (auto it = m_Arrows.begin(); it != m_Arrows.end(); ++it)
|
||||
{
|
||||
if (it->second.Focus())
|
||||
{
|
||||
m_Arrows.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
SetFocus(index);
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invert the values of all colors by subtracting each component from 255.
|
||||
/// </summary>
|
||||
void GradientColorsView::InvertColors()
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& arrow = it.second;
|
||||
auto col = arrow.Color();
|
||||
arrow.Color(QColor(255 - col.red(), 255 - col.green(), 255 - col.blue()));
|
||||
|
||||
if (arrow.Focus())
|
||||
emit ArrowDoubleClicked(arrow);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set each component of each color to a random value between 0 and 255 inclusive.
|
||||
/// </summary>
|
||||
void GradientColorsView::RandomColors()
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
it.second.Color(
|
||||
{
|
||||
int(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(256)),
|
||||
int(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(256)),
|
||||
int(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(256))
|
||||
});
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the distance between each arrow to be equal.
|
||||
/// </summary>
|
||||
void GradientColorsView::DistributeColors()
|
||||
{
|
||||
map<float, GradientArrow> arrows;
|
||||
float index = 0, inc = 1.0f / std::max<size_t>(size_t(1), m_Arrows.size() - 1);
|
||||
|
||||
for (auto it : m_Arrows)
|
||||
{
|
||||
arrows[index] = it.second;
|
||||
index = std::min(1.0f, index + inc);
|
||||
}
|
||||
|
||||
m_Arrows = std::move(arrows);
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete all arrows and add a white arrow at index 0, and a black
|
||||
/// arrow at index 1.
|
||||
/// </summary>
|
||||
void GradientColorsView::ResetToDefault()
|
||||
{
|
||||
ClearArrows();
|
||||
AddArrow(0.0, Qt::white);
|
||||
AddArrow(1.0, Qt::black);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all arrows.
|
||||
/// </summary>
|
||||
void GradientColorsView::ClearArrows()
|
||||
{
|
||||
m_Arrows.clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the arrow at the specified index to the specified color, and also
|
||||
/// focus it.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to assign to the arrow at the specified index</param>
|
||||
/// <param name="index">The index of the arrow to assign the color to and focus</param>
|
||||
void GradientColorsView::NewFocusColor(const QColor& color, int index)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (auto& kv : m_Arrows)
|
||||
{
|
||||
auto& arrow = kv.second;
|
||||
|
||||
if (i == index)
|
||||
{
|
||||
arrow.Color(color);
|
||||
arrow.Focus(true);
|
||||
update();
|
||||
}
|
||||
else
|
||||
arrow.Focus(false);
|
||||
|
||||
kv.second = arrow;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the arrow map to the passed in one.
|
||||
/// </summary>
|
||||
/// <param name="newArrows">The new arrows to assign to the internal m_Arrows member</param>
|
||||
void GradientColorsView::SetArrows(map<float, GradientArrow>& newArrows)
|
||||
{
|
||||
m_Arrows = newArrows;
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of arrows in the map.
|
||||
/// </summary>
|
||||
/// <returns>int</returns>
|
||||
int GradientColorsView::ArrowCount()
|
||||
{
|
||||
return int(m_Arrows.size());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the focused arrow.
|
||||
/// Return 0 if none are focused.
|
||||
/// </summary>
|
||||
/// <returns>The focused index if at least one arrow is focused, else 0.</returns>
|
||||
int GradientColorsView::GetFocusedIndex()
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
for (auto& kv : m_Arrows)
|
||||
{
|
||||
if (kv.second.Focus())
|
||||
break;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a pixmap to be used to draw the palette.
|
||||
/// The pixmap is lazily instantiated on the first call, and all subsequent
|
||||
/// calls return a pointer to the same pixmap.
|
||||
/// </summary>
|
||||
/// <returns>The pixmap</returns>
|
||||
QPixmap* GradientColorsView::GetBackGround()
|
||||
{
|
||||
if (!m_Background.get())
|
||||
CreateBackground(m_BackgroundVerSpace, m_BackgroundHorSpace);
|
||||
|
||||
return m_Background.get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a reference to the arrows map.
|
||||
/// Be very careful what you do with this.
|
||||
/// </summary>
|
||||
/// <returns>A reference to the internal map containing the arrows</returns>
|
||||
map<float, GradientArrow>& GradientColorsView::GetArrows()
|
||||
{
|
||||
return m_Arrows;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the palette member with the specified number of elements based on
|
||||
/// interpolating the values in the arrows and return a reference to it.
|
||||
/// </summary>
|
||||
/// <param name="size">The number of elements the palette will have</param>
|
||||
/// <returns>A reference to the internal map containing the arrows</returns>
|
||||
Palette<float>& GradientColorsView::GetPalette(int size)
|
||||
{
|
||||
QSize imageSize(size, 1);
|
||||
QImage image(imageSize, QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter p;
|
||||
QLinearGradient grad(QPoint(0, 0), QPoint(imageSize.width(), imageSize.height()));
|
||||
m_Palette.m_SourceColors.clear();
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
QColor col(image.pixel(i, 0));
|
||||
m_Palette[i].r = col.red() / 255.0f;
|
||||
m_Palette[i].g = col.green() / 255.0f;
|
||||
m_Palette[i].b = col.blue() / 255.0f;
|
||||
}
|
||||
|
||||
return m_Palette;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign the values of the m_SourceColors member of the palette to the
|
||||
/// internal map of arrows. Note this assignment will only take place if
|
||||
/// the number of source colors is 2 or more.
|
||||
/// This will only be the case if it was a user created palette made here.
|
||||
/// All palettes gotten from elsewhere are not assignable.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette whose source colors will be assigned to the arrow map</param>
|
||||
void GradientColorsView::SetPalette(const Palette<float>& palette)
|
||||
{
|
||||
if (palette.m_SourceColors.size() > 1)
|
||||
{
|
||||
m_Palette = palette;
|
||||
m_Arrows.clear();
|
||||
|
||||
for (auto& col : m_Palette.m_SourceColors)
|
||||
{
|
||||
auto& rgb = col.second;
|
||||
m_Arrows[col.first] = GradientArrow(QColor(rgb.r * 255, rgb.g * 255, rgb.b * 255), false);
|
||||
}
|
||||
|
||||
SetFocus(size_t(0));
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom paint event to draw the palette and arrows.
|
||||
/// </summary>
|
||||
void GradientColorsView::paintEvent(QPaintEvent*)
|
||||
{
|
||||
if (m_ViewRect.size().isNull() ||
|
||||
m_ViewRect.size().isEmpty() ||
|
||||
m_ViewRect.topLeft() == m_ViewRect.bottomRight())
|
||||
{
|
||||
m_ViewRect = QRect(QPoint(5, 0), QPoint(width() - 15, height() / 3 * 2 - 10));
|
||||
m_ViewRect.translate(5, 5);
|
||||
CreateBackground();
|
||||
}
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
if (m_Background.get())
|
||||
painter.drawPixmap(m_ViewRect, *m_Background.get(), m_ViewRect);
|
||||
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
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);
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
GradientArrow& arrow = it.second;
|
||||
grad.setColorAt(it.first, arrow.Color());
|
||||
QPolygon arrowPolygon = arrow.Area();
|
||||
int iPosX = it.first * (width() - 20),
|
||||
iPosY = height() / 3 * 2;
|
||||
arrowPolygon.translate(iPosX, iPosY);
|
||||
QPainterPath paintPath;
|
||||
paintPath.addPolygon(arrowPolygon);
|
||||
painter.setBrush(QBrush(arrow.Color()));
|
||||
|
||||
if (arrow.Focus())
|
||||
paintPath.addRect(iPosX + 5, iPosY + 20, 10, 5);
|
||||
|
||||
painter.drawPath(paintPath);
|
||||
painter.setBrush(QBrush(Qt::NoBrush));
|
||||
}
|
||||
|
||||
QBrush brush(grad);
|
||||
painter.fillRect(m_ViewRect, brush);
|
||||
painter.drawRect(m_ViewRect);
|
||||
painter.end();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is pressed on an arrow to begin dragging.
|
||||
/// </summary>
|
||||
/// <param name="e">The mouse event</param>
|
||||
void GradientColorsView::mousePressEvent(QMouseEvent* e)
|
||||
{
|
||||
m_DragStart = e->pos();
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& arrow = it.second;
|
||||
QPolygon poly = arrow.Area();
|
||||
poly.translate(it.first * (width() - 20), height() / 3 * 2);
|
||||
|
||||
if (poly.containsPoint(m_DragStart, Qt::OddEvenFill))
|
||||
{
|
||||
m_ArrowMoving = true;
|
||||
arrow.Focus(true);
|
||||
}
|
||||
else
|
||||
arrow.Focus(false);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is pressed on an arrow to begin dragging.
|
||||
/// </summary>
|
||||
/// <param name="event">The mouse event</param>
|
||||
void GradientColorsView::mouseDoubleClickEvent(QMouseEvent* e)
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& arrow = it.second;
|
||||
QPolygon poly = arrow.Area();
|
||||
poly.translate(it.first * (width() - 20), height() / 3 * 2);
|
||||
|
||||
if (poly.containsPoint(e->pos(), Qt::OddEvenFill))
|
||||
{
|
||||
arrow.Focus(true);
|
||||
emit ArrowDoubleClicked(arrow);
|
||||
}
|
||||
else
|
||||
arrow.Focus(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is moving during dragging.
|
||||
/// </summary>
|
||||
/// <param name="event">The mouse event</param>
|
||||
void GradientColorsView::mouseMoveEvent(QMouseEvent* e)
|
||||
{
|
||||
if (!m_ArrowMoving) return;
|
||||
|
||||
size_t index = 0;
|
||||
qreal maxMove = 11.5 / (width() - 20);
|
||||
|
||||
for (auto it = m_Arrows.begin(); it != m_Arrows.end(); ++it)
|
||||
{
|
||||
auto& arrow = it->second;
|
||||
|
||||
if (arrow.Focus())
|
||||
{
|
||||
qreal lastPos = it->first;
|
||||
qreal start = m_DragStart.x();
|
||||
qreal end = width() - 20;
|
||||
qreal dPos = ((qreal) e->pos().x() - start) / end;
|
||||
qreal newPos = lastPos + dPos;
|
||||
|
||||
if ( (it->first + dPos > 1) || (it->first + dPos < 0) )
|
||||
return;
|
||||
|
||||
if (dPos < 0 && index > 0)
|
||||
{
|
||||
qreal posBefore = std::prev(it)->first;
|
||||
|
||||
if ( (lastPos - maxMove + dPos) <= posBefore )
|
||||
return;
|
||||
}
|
||||
|
||||
if ((dPos > 0) && (index < (m_Arrows.size() - 1)))
|
||||
{
|
||||
qreal posAfter = std::next(it)->first;
|
||||
|
||||
if ((lastPos + maxMove + dPos) >= posAfter)
|
||||
return;
|
||||
}
|
||||
|
||||
GradientArrow arrowCopy(it->second);
|
||||
m_Arrows.erase(lastPos);
|
||||
m_Arrows[newPos] = arrowCopy;
|
||||
emit ArrowMove(lastPos, arrow);
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
m_DragStart = e->pos();
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is released during dragging.
|
||||
/// </summary>
|
||||
void GradientColorsView::mouseReleaseEvent(QMouseEvent*)
|
||||
{
|
||||
m_ArrowMoving = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for custom drawing the viewable area when its resized.
|
||||
/// </summary>
|
||||
void GradientColorsView::resizeEvent(QResizeEvent*)
|
||||
{
|
||||
m_ViewRect = QRect(QPoint(5, 0), QPoint(width() - 15, height() / 3 * 2 - 10));
|
||||
m_ViewRect.translate(5, 5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the background to represent the palette.
|
||||
/// </summary>
|
||||
/// <param name="vertLineSpace">The space between vertical lines to use</param>
|
||||
/// <param name="horLineSpace">The space between horizontal lines to use</param>
|
||||
void GradientColorsView::CreateBackground(int vertLineSpace, int horLineSpace)
|
||||
{
|
||||
m_BackgroundVerSpace = vertLineSpace;
|
||||
m_BackgroundHorSpace = horLineSpace;
|
||||
m_Background = make_unique<QPixmap>(QSize(800, 800));
|
||||
m_Background->fill(Qt::white);
|
||||
QPainter painter(m_Background.get());
|
||||
int x = 0;
|
||||
|
||||
while (x < m_Background->width())//Veritcal lines.
|
||||
{
|
||||
const QPoint lineStart(x, 0);
|
||||
const QPoint lineStop(x, m_Background->height());
|
||||
painter.drawLine(lineStart, lineStop);
|
||||
x += vertLineSpace;
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
|
||||
while (y < m_Background->height())//Horizontal lines.
|
||||
{
|
||||
const QPoint lineStart(0, y);
|
||||
const QPoint lineStop(m_Background->width(), y);
|
||||
painter.drawLine(lineStart, lineStop);
|
||||
y += horLineSpace;
|
||||
}
|
||||
|
||||
painter.end();
|
||||
update();
|
||||
}
|
80
Source/Fractorium/PaletteEditor/GradientColorsView.h
Normal file
80
Source/Fractorium/PaletteEditor/GradientColorsView.h
Normal file
@ -0,0 +1,80 @@
|
||||
/****************************************************************************/
|
||||
// This file is part of the gradLib library originally made by Stian Broen
|
||||
//
|
||||
// For more free libraries, please visit <http://broentech.no>
|
||||
//
|
||||
// gradLib is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this library. If not, see <http://www.gnu.org/licenses/>
|
||||
/****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "GradientArrow.h"
|
||||
|
||||
/// <summary>
|
||||
/// Class for drawing the resulting palette created by interpolating the key colors
|
||||
/// as well as the arrows underneath the palette.
|
||||
/// The arrows are held in a sorted map whose key is the normalized index of the arrow,
|
||||
/// between 0 and 1 inclusive. They value is the arrow itself.
|
||||
/// The resulting palette is always stored in the m_Palette member.
|
||||
/// </summary>
|
||||
class GradientColorsView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GradientColorsView(QWidget* p = nullptr);
|
||||
void SetFocus(float position);
|
||||
void SetFocus(size_t position);
|
||||
void SetFocusColor(const QColor& col);
|
||||
void AddArrow(const QColor& color);
|
||||
void AddArrow(float position, const QColor& color);
|
||||
void DeleteFocusedArrow();
|
||||
void InvertColors();
|
||||
void RandomColors();
|
||||
void DistributeColors();
|
||||
void ResetToDefault();
|
||||
void ClearArrows();
|
||||
void NewFocusColor(const QColor& col, int index);
|
||||
void SetArrows(map<float, GradientArrow>& newArrows);
|
||||
int ArrowCount();
|
||||
int GetFocusedIndex();
|
||||
QPixmap* GetBackGround();
|
||||
map<float, GradientArrow>& GetArrows();
|
||||
Palette<float>& GetPalette(int size);
|
||||
void SetPalette(const Palette<float>& palette);
|
||||
|
||||
signals:
|
||||
void ArrowMove(qreal lastPos, const GradientArrow& arrow);
|
||||
void ArrowDoubleClicked(const GradientArrow& arrow);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* e) override;
|
||||
virtual void mousePressEvent(QMouseEvent* e) override;
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent* e) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent* e) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* e) override;
|
||||
virtual void resizeEvent(QResizeEvent*) override;
|
||||
|
||||
private:
|
||||
void CreateBackground(int vertLineSpace = 5, int horLineSpace = 5);
|
||||
bool m_ArrowMoving = false;
|
||||
int m_BackgroundVerSpace = 5;
|
||||
int m_BackgroundHorSpace = 5;
|
||||
QRect m_ViewRect;
|
||||
QPoint m_DragStart;
|
||||
unique_ptr<QPixmap> m_Background;
|
||||
map<float, GradientArrow> m_Arrows;
|
||||
Palette<float> m_Palette;
|
||||
};
|
498
Source/Fractorium/PaletteEditor/PaletteEditor.cpp
Normal file
498
Source/Fractorium/PaletteEditor/PaletteEditor.cpp
Normal file
@ -0,0 +1,498 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "PaletteEditor.h"
|
||||
#include "ui_paletteeditor.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and sets up slots and other ui
|
||||
/// elements.
|
||||
/// This takes a reference to a palette list, which it then searches for user created palettes
|
||||
/// and only populates the combo box with those.
|
||||
/// </summary>
|
||||
/// <param name="paletteList">A reference to an existing palette list, gotten from the main window.</param>
|
||||
/// <param name="p">The parent widget</param>
|
||||
PaletteEditor::PaletteEditor(PaletteList<float>& paletteList, QWidget* p) :
|
||||
QDialog(p),
|
||||
ui(make_unique<Ui::PaletteEditor>()),
|
||||
m_PaletteList(paletteList)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ColorPicker = new ColorPickerWidget(this);
|
||||
m_ColorPicker->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
m_ColorPicker->SetColorPanelColor(Qt::black);
|
||||
QVBoxLayout* colorLayout = new QVBoxLayout();
|
||||
colorLayout->setMargin(3);
|
||||
colorLayout->addWidget(m_ColorPicker);
|
||||
ui->ColorPickerGroupBox->setLayout(colorLayout);
|
||||
ui->ColorPickerGroupBox->setContentsMargins(3, 8, 3, 3);
|
||||
m_GradientColorView = new GradientColorsView(ui->ColorViewGroupBox);
|
||||
connect(m_ColorPicker, SIGNAL(ColorChanged(const QColor&)), this, SLOT(OnColorPickerColorChanged(const QColor&)));
|
||||
connect(m_GradientColorView, SIGNAL(ArrowMove(qreal, const GradientArrow&)), this, SLOT(OnArrowMoved(qreal, const GradientArrow&)));
|
||||
connect(m_GradientColorView, SIGNAL(ArrowDoubleClicked(const GradientArrow&)), this, SLOT(OnArrowDoubleClicked(const GradientArrow&)));
|
||||
connect(ui->CreatePaletteFromImageButton, SIGNAL(clicked()), this, SLOT(OnCreatePaletteFromImageButtonClicked()));
|
||||
connect(ui->CreatePaletteAgainFromImageButton, SIGNAL(clicked()), this, SLOT(OnCreatePaletteAgainFromImageButton()));
|
||||
connect(ui->AddColorButton, SIGNAL(clicked()), this, SLOT(OnAddColorButtonClicked()));
|
||||
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->SyncCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSyncCheckBoxStateChanged(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);
|
||||
connect(ui->NewPaletteFileButton, SIGNAL(clicked()), this, SLOT(OnNewPaletteFileButtonClicked()));
|
||||
connect(ui->CopyPaletteFileButton, SIGNAL(clicked()), this, SLOT(OnCopyPaletteFileButtonClicked()));
|
||||
connect(ui->AppendPaletteButton, SIGNAL(clicked()), this, SLOT(OnAppendPaletteButtonClicked()));
|
||||
connect(ui->OverwritePaletteButton, SIGNAL(clicked()), this, SLOT(OnOverwritePaletteButtonClicked()));
|
||||
connect(ui->DeletePaletteButton, SIGNAL(clicked()), this, SLOT(OnDeletePaletteButtonClicked()));
|
||||
ui->PaletteListTable->horizontalHeader()->setSectionsClickable(true);
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(m_GradientColorView);
|
||||
ui->ColorViewGroupBox->setLayout(layout);
|
||||
auto& pals = m_PaletteList.Palettes();
|
||||
|
||||
for (auto& pal : pals)
|
||||
{
|
||||
if (m_PaletteList.IsModifiable(pal.first))//Only add user created palettes.
|
||||
{
|
||||
QFileInfo info(QString::fromStdString(pal.first));
|
||||
ui->PaletteFilenameCombo->addItem(info.fileName());
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->PaletteFilenameCombo->count() > 0)
|
||||
m_CurrentPaletteFilePath = ui->PaletteFilenameCombo->itemText(0).toStdString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get whether change events here are propagated back to the main window.
|
||||
/// </summary>
|
||||
/// <returns>bool</returns>
|
||||
bool PaletteEditor::Sync()
|
||||
{
|
||||
return ui->SyncCheckBox->isChecked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to the palette pixmap from the underlying gradient color view.
|
||||
/// </summary>
|
||||
/// <returns>QPixmap*</returns>
|
||||
QPixmap* PaletteEditor::GetBackGround()
|
||||
{
|
||||
return m_GradientColorView->GetBackGround();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate and retrieve a reference to the palette from the underlying gradient color view
|
||||
/// using the specified number of elements.
|
||||
/// </summary>
|
||||
/// <param name="size">The number of elements the palette will have</param>
|
||||
/// <returns>A freference to the palette</returns>
|
||||
Palette<float>& PaletteEditor::GetPalette(int size)
|
||||
{
|
||||
return m_GradientColorView->GetPalette(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the palette of the underlying gradient color view.
|
||||
/// Note this assignment will only take place if
|
||||
/// the number of source colors is 2 or more.
|
||||
/// This will only be the case if it was a user created palette made here.
|
||||
/// All palettes gotten from elsewhere are not assignable.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette to assign</param>
|
||||
void PaletteEditor::SetPalette(Palette<float>& palette)
|
||||
{
|
||||
if (palette.m_SourceColors.size() > 1)
|
||||
{
|
||||
m_PaletteIndex = std::numeric_limits<int>::max();
|
||||
m_GradientColorView->SetPalette(palette);
|
||||
auto& arrows = m_GradientColorView->GetArrows();
|
||||
|
||||
if (!arrows.empty())
|
||||
m_ColorPicker->SetColorPanelColor(arrows.begin()->second.Color());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new arrow using the current color.
|
||||
/// Called when the Add Color button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnAddColorButtonClicked()
|
||||
{
|
||||
AddArrow(m_ColorPicker->Color());
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the focused arrow and optionally redistribute the arrows.
|
||||
/// Called when the Remove Color button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnRemoveColorButtonClicked()
|
||||
{
|
||||
m_GradientColorView->DeleteFocusedArrow();
|
||||
|
||||
if (ui->AutoDistributeCheckBox->isChecked())
|
||||
m_GradientColorView->DistributeColors();
|
||||
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invert the colors of the arrows.
|
||||
/// Called when the Invert Colors button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnInvertColorsButtonClicked()
|
||||
{
|
||||
m_GradientColorView->InvertColors();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Randomize the colors of the arrows.
|
||||
/// Called when the Random Colors button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnRandomColorsButtonClicked()
|
||||
{
|
||||
m_GradientColorView->RandomColors();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the distance between each arrow to be equal.
|
||||
/// Called when the Distribute Colors button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnDistributeColorsButtonClicked()
|
||||
{
|
||||
m_GradientColorView->DistributeColors();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete all arrows and add a white arrow at index 0, and a black
|
||||
/// arrow at index 1.
|
||||
/// Called when the Reset button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnResetToDefaultButtonClicked()
|
||||
{
|
||||
m_GradientColorView->ResetToDefault();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a palette by opening an image and selecting the colors from
|
||||
/// pixels at random locations.
|
||||
/// Ensure arrows spin box has a value of at least two.
|
||||
/// Called when the From Image button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnCreatePaletteFromImageButtonClicked()
|
||||
{
|
||||
auto filenames = SetupOpenImagesDialog();
|
||||
|
||||
if (!filenames.empty())
|
||||
{
|
||||
m_Filename = filenames[0];
|
||||
|
||||
if (ui->ArrowsSpinBox->value() < 2)
|
||||
ui->ArrowsSpinBox->setValue(2);
|
||||
|
||||
auto colors = GetRandomColorsFromImage(m_Filename, ui->ArrowsSpinBox->value());
|
||||
m_GradientColorView->SetArrows(colors);
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a palette by re-opening the previously selected image and selecting the colors from
|
||||
/// pixels at random locations.
|
||||
/// Ensure arrows spin box has a value of at least two.
|
||||
/// Called when the From Image Again button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnCreatePaletteAgainFromImageButton()
|
||||
{
|
||||
if (QFile::exists(m_Filename))
|
||||
{
|
||||
if (ui->ArrowsSpinBox->value() < 2)
|
||||
ui->ArrowsSpinBox->setValue(2);
|
||||
|
||||
auto colors = GetRandomColorsFromImage(m_Filename, ui->ArrowsSpinBox->value());
|
||||
m_GradientColorView->SetArrows(colors);
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the focus color as a result of selecting a stock in the color picker.
|
||||
/// Called when the color picker signals the ColorChanged event.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnColorPickerColorChanged(const QColor& col)
|
||||
{
|
||||
m_GradientColorView->SetFocusColor(col);
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the color panel color as a result of double clicking an arrow.
|
||||
/// Called when the color view signals the ArrowDoubleClicked event.
|
||||
/// </summary>
|
||||
/// <param name="arrow">The arrow which was double clicked on</param>
|
||||
void PaletteEditor::OnArrowDoubleClicked(const GradientArrow& arrow)
|
||||
{
|
||||
m_ColorPicker->SetColorPanelColor(arrow.Color());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change whether palette changes are synced with the main window.
|
||||
/// Called when the Sync checkbox is checked/unchecked.
|
||||
/// </summary>
|
||||
/// <param name="state">Ignored</param>
|
||||
void PaletteEditor::OnSyncCheckBoxStateChanged(int state)
|
||||
{
|
||||
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.
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the combo box, which is just the palette filename without the path.</param>
|
||||
void PaletteEditor::OnPaletteFilenameComboChanged(const QString& text)
|
||||
{
|
||||
if (!text.isEmpty())//This occasionally seems to get called with an empty string for reasons unknown.
|
||||
{
|
||||
auto paletteTable = ui->PaletteListTable;
|
||||
m_CurrentPaletteFilePath = text.toStdString();
|
||||
::FillPaletteTable(text.toStdString(), paletteTable, m_PaletteList);
|
||||
auto fullname = m_PaletteList.GetFullPathFromFilename(m_CurrentPaletteFilePath);
|
||||
ui->PaletteFilenameCombo->setToolTip(QString::fromStdString(fullname));
|
||||
OnPaletteCellClicked(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the palette into the editor controls.
|
||||
/// Called when the second column in a row in the palette list is clicked.
|
||||
/// </summary>
|
||||
/// <param name="row">The table row clicked</param>
|
||||
/// <param name="col">The table column clicked</param>
|
||||
void PaletteEditor::OnPaletteCellClicked(int row, int col)
|
||||
{
|
||||
if (col == 1)
|
||||
{
|
||||
if (auto palette = m_PaletteList.GetPaletteByFilename(m_CurrentPaletteFilePath, row))
|
||||
{
|
||||
SetPalette(*palette);
|
||||
m_PaletteIndex = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the name of the palette.
|
||||
/// Called when the first column in a row in the palette list is clicked and edited.
|
||||
/// </summary>
|
||||
/// <param name="row">The table row clicked</param>
|
||||
/// <param name="col">The table column clicked</param>
|
||||
void PaletteEditor::OnPaletteCellChanged(int row, int col)
|
||||
{
|
||||
if (col == 0)
|
||||
{
|
||||
if (auto palette = m_PaletteList.GetPaletteByFilename(m_CurrentPaletteFilePath, row))
|
||||
{
|
||||
palette->m_Name = ui->PaletteListTable->item(row, col)->text().toStdString();
|
||||
emit PaletteFileChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new palette file.
|
||||
/// The newly created file will have a unique name.
|
||||
/// Called when the new palette file button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnNewPaletteFileButtonClicked()
|
||||
{
|
||||
auto filename = EmberFile<float>::UniqueFilename(GetDefaultUserPath() + "/user-palettes.xml");
|
||||
|
||||
if (m_PaletteList.AddEmptyPaletteFile(filename.toStdString()))
|
||||
{
|
||||
QFileInfo info(filename);
|
||||
ui->PaletteFilenameCombo->addItem(info.fileName());
|
||||
ui->PaletteFilenameCombo->setCurrentIndex(ui->PaletteFilenameCombo->count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the current palette file, add it to the combo box and load the new palette file.
|
||||
/// The newly created file will have a unique name.
|
||||
/// Called when the copy palette file button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnCopyPaletteFileButtonClicked()
|
||||
{
|
||||
auto& paletteFiles = m_PaletteList.Palettes();
|
||||
auto qscurr = QString::fromStdString(m_CurrentPaletteFilePath);
|
||||
auto qfilename = EmberFile<float>::UniqueFilename(GetDefaultUserPath() + "/" + qscurr);
|
||||
auto filename = qfilename.toStdString();
|
||||
|
||||
if (m_PaletteList.GetPaletteListByFullPath(filename) == nullptr)//Ensure the new filename does not exist in the map.
|
||||
{
|
||||
//Get the list of palettes for the current filename, this will be added as a copy below.
|
||||
if (auto currentPaletteFile = m_PaletteList.GetPaletteListByFilename(m_CurrentPaletteFilePath))//Ensure the list of palettes was properly retrieved.
|
||||
{
|
||||
if (m_PaletteList.AddPaletteFile(filename, *currentPaletteFile))//Add the current vector of palettes to an entry with the new filename.
|
||||
{
|
||||
QFileInfo info(qfilename);
|
||||
ui->PaletteFilenameCombo->addItem(info.fileName());
|
||||
ui->PaletteFilenameCombo->setCurrentIndex(ui->PaletteFilenameCombo->count() - 1);
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, "Copy palette file error", "Failed copy palette to " + qfilename + ", because already exists in memory, but not on disk. Did you delete it while the program was running?");
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, "Copy palette file error", "The current file " + qscurr + " did not exist. Did you delete it while the program was running?");
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, "Copy palette file error", "Failed copy palette to " + qfilename + ", because it likely already exists");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the current palette to the end of the palette file.
|
||||
/// Called when the append palette button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnAppendPaletteButtonClicked()
|
||||
{
|
||||
auto& pal = m_GradientColorView->GetPalette(256);
|
||||
m_PaletteList.AddPaletteToFile(m_CurrentPaletteFilePath, pal);
|
||||
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
|
||||
m_PaletteIndex = ui->PaletteListTable->rowCount() - 1;
|
||||
emit PaletteFileChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overwrite the current palette in the palette file.
|
||||
/// Called when the overwrite palette button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnOverwritePaletteButtonClicked()
|
||||
{
|
||||
auto& pal = m_GradientColorView->GetPalette(256);
|
||||
m_PaletteList.Replace(m_CurrentPaletteFilePath, pal, m_PaletteIndex);
|
||||
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
|
||||
emit PaletteFileChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the current palette from the palette file.
|
||||
/// Called when the delete palette button is clicked.
|
||||
/// Note that the palette will not be deleted if it's the only palette in the file.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnDeletePaletteButtonClicked()
|
||||
{
|
||||
auto table = ui->PaletteListTable;
|
||||
|
||||
if (table->rowCount() > 1)
|
||||
{
|
||||
m_PaletteList.Delete(m_CurrentPaletteFilePath, m_PaletteIndex);
|
||||
::FillPaletteTable(m_CurrentPaletteFilePath, table, m_PaletteList);
|
||||
emit PaletteFileChanged();
|
||||
OnPaletteCellClicked(0, table->rowCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a palette changed event.
|
||||
/// Called when an arrow is moved.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnArrowMoved(qreal, const GradientArrow&)
|
||||
{
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a palette changed event if the sync checkbox is checked.
|
||||
/// </summary>
|
||||
void PaletteEditor::EmitPaletteChanged()
|
||||
{
|
||||
if (ui->SyncCheckBox->isChecked())
|
||||
emit PaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to lazily instantiate an open file dialog.
|
||||
/// Once created, it will remain alive for the duration of the program run.
|
||||
/// </summary>
|
||||
QStringList PaletteEditor::SetupOpenImagesDialog()
|
||||
{
|
||||
if (!m_FileDialog)
|
||||
{
|
||||
m_FileDialog = new QFileDialog(this);
|
||||
m_FileDialog->setViewMode(QFileDialog::List);
|
||||
m_FileDialog->setFileMode(QFileDialog::ExistingFile);
|
||||
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
|
||||
m_FileDialog->setNameFilter("Image Files (*.png *.jpg *.bmp)");
|
||||
m_FileDialog->setWindowTitle("Open Image");
|
||||
m_FileDialog->setDirectory(QCoreApplication::applicationDirPath());
|
||||
m_FileDialog->selectNameFilter("*.jpg");
|
||||
}
|
||||
|
||||
QStringList filenames;
|
||||
|
||||
if (m_FileDialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
filenames = m_FileDialog->selectedFiles();
|
||||
|
||||
if (!filenames.empty())
|
||||
m_FileDialog->setDirectory(QFileInfo(filenames[0]).canonicalPath());
|
||||
}
|
||||
|
||||
return filenames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an arrow whose color will be assigned the passed in color.
|
||||
/// Optionally distribute colors and emit a palette changed event.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to assign to the new arrow</param>
|
||||
void PaletteEditor::AddArrow(const QColor& color)
|
||||
{
|
||||
auto count = std::min<int>(std::abs(256 - m_GradientColorView->ArrowCount()), ui->ArrowsSpinBox->value());
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
m_GradientColorView->AddArrow(color);
|
||||
|
||||
if (ui->AutoDistributeCheckBox->isChecked())
|
||||
m_GradientColorView->DistributeColors();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to get the colors of random pixels within an image.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the image file to get random colors from</param>
|
||||
/// <param name="numPoints">The number of colors to get</param>
|
||||
map<float, GradientArrow> PaletteEditor::GetRandomColorsFromImage(QString filename, int numPoints)
|
||||
{
|
||||
map<float, GradientArrow> colors;
|
||||
QTime time = QTime::currentTime();
|
||||
qsrand((uint)time.msec());
|
||||
QImage image(filename);
|
||||
const qreal gSize = 512;
|
||||
float off = 0.0f, inc = 1.0f / std::max(1, numPoints - 1);
|
||||
QLinearGradient grad(QPoint(0, 0), QPoint(gSize, 1));
|
||||
|
||||
for (int i = 0; i < numPoints; i++)
|
||||
{
|
||||
int x = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % image.width();
|
||||
int y = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % image.height();
|
||||
QRgb rgb = image.pixel(x, y);
|
||||
GradientArrow arrow;
|
||||
arrow.Color(QColor::fromRgb(rgb));
|
||||
arrow.Focus(i == 0);
|
||||
colors[off] = arrow;
|
||||
off += inc;
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
78
Source/Fractorium/PaletteEditor/PaletteEditor.h
Normal file
78
Source/Fractorium/PaletteEditor/PaletteEditor.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorPickerWidget.h"
|
||||
#include "GradientColorsView.h"
|
||||
#include "EmberFile.h"
|
||||
#include "ui_PaletteEditor.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PaletteEditor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dialog for editing user created palettes.
|
||||
/// This will load with all available user created palettes populated in the combo
|
||||
/// box. As the user changes the selected index, the palettes for that file
|
||||
/// are shown in the list box. The user can click on those and then edit them and either
|
||||
/// save it back to the same position in the file, or append it to the end.
|
||||
/// They can also click in the name column to set/rename the palette name.
|
||||
/// Any changes on this dialog can be "synced". That is, when the Sync checkbox is checked
|
||||
/// any changes result in a signal being sent back to the main window.
|
||||
/// </summary>
|
||||
class PaletteEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PaletteEditor(PaletteList<float>& paletteList, QWidget* p = nullptr);
|
||||
|
||||
public:
|
||||
bool Sync();
|
||||
QPixmap* GetBackGround();
|
||||
Palette<float>& GetPalette(int size);
|
||||
void SetPalette(Palette<float>& palette);
|
||||
|
||||
signals:
|
||||
void PaletteChanged();
|
||||
void PaletteFileChanged();
|
||||
|
||||
private slots:
|
||||
void OnAddColorButtonClicked();
|
||||
void OnRemoveColorButtonClicked();
|
||||
void OnInvertColorsButtonClicked();
|
||||
void OnRandomColorsButtonClicked();
|
||||
void OnDistributeColorsButtonClicked();
|
||||
void OnResetToDefaultButtonClicked();
|
||||
void OnCreatePaletteFromImageButtonClicked();
|
||||
void OnCreatePaletteAgainFromImageButton();
|
||||
void OnColorPickerColorChanged(const QColor& col);
|
||||
void OnArrowDoubleClicked(const GradientArrow& arrow);
|
||||
void OnSyncCheckBoxStateChanged(int state);
|
||||
void OnArrowMoved(qreal lastPos, const GradientArrow& arrow);
|
||||
void OnPaletteFilenameComboChanged(const QString& text);
|
||||
void OnPaletteCellClicked(int row, int col);
|
||||
void OnPaletteCellChanged(int row, int col);
|
||||
void OnNewPaletteFileButtonClicked();
|
||||
void OnCopyPaletteFileButtonClicked();
|
||||
void OnAppendPaletteButtonClicked();
|
||||
void OnOverwritePaletteButtonClicked();
|
||||
void OnDeletePaletteButtonClicked();
|
||||
|
||||
private:
|
||||
void EmitPaletteChanged();
|
||||
QStringList SetupOpenImagesDialog();
|
||||
void AddArrow(const QColor& color);
|
||||
map<float, GradientArrow> GetRandomColorsFromImage(QString filename, int numPoints);
|
||||
|
||||
bool m_PaletteFileChanged = false;
|
||||
int m_PaletteIndex = 0;
|
||||
QString m_Filename;
|
||||
string m_CurrentPaletteFilePath;
|
||||
ColorPickerWidget* m_ColorPicker = nullptr;
|
||||
GradientColorsView* m_GradientColorView = nullptr;
|
||||
QFileDialog* m_FileDialog = nullptr;
|
||||
PaletteList<float>& m_PaletteList;
|
||||
std::unique_ptr<Ui::PaletteEditor> ui;
|
||||
};
|
@ -11,28 +11,17 @@
|
||||
/// The lifetime of the palette object must be greater than or equal to
|
||||
/// the lifetime of this object.
|
||||
/// </summary>
|
||||
class PaletteTableWidgetItemBase : public QTableWidgetItem
|
||||
class PaletteTableWidgetItem : public QTableWidgetItem
|
||||
{
|
||||
public:
|
||||
PaletteTableWidgetItemBase()
|
||||
{
|
||||
}
|
||||
|
||||
virtual size_t Index() const { return 0; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class PaletteTableWidgetItem : public PaletteTableWidgetItemBase
|
||||
{
|
||||
public:
|
||||
PaletteTableWidgetItem(Palette<T>* palette)
|
||||
PaletteTableWidgetItem(Palette<float>* palette)
|
||||
: m_Palette(palette)
|
||||
{
|
||||
}
|
||||
|
||||
virtual size_t Index() const override { return m_Palette->m_Index; }
|
||||
Palette<T>* GetPalette() const { return m_Palette; }
|
||||
size_t Index() const { return m_Palette->m_Index; }
|
||||
Palette<float>* GetPalette() const { return m_Palette; }
|
||||
|
||||
private:
|
||||
Palette<T>* m_Palette;
|
||||
Palette<float>* m_Palette;
|
||||
};
|
@ -17,10 +17,12 @@ SpinBox::SpinBox(QWidget* p, int h, int step)
|
||||
: QSpinBox(p)
|
||||
{
|
||||
m_DoubleClick = false;
|
||||
m_DoubleClickLowVal = 0;
|
||||
m_DoubleClickNonZero = 0;
|
||||
m_DoubleClickZero = 1;
|
||||
m_Step = step;
|
||||
m_SmallStep = 1;
|
||||
m_Settings = FractoriumSettings::DefInstance();
|
||||
setSingleStep(step);
|
||||
setFrame(false);
|
||||
setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
@ -54,6 +56,16 @@ void SpinBox::DoubleClick(bool b)
|
||||
m_DoubleClick = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used instead of zero to represent the lower value
|
||||
/// used when responding to a double click.
|
||||
/// </summary>
|
||||
/// <param name="val">The value to be used for the lower value instead of zero</param>
|
||||
void SpinBox::DoubleClickLowVal(int val)
|
||||
{
|
||||
m_DoubleClickLowVal = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used when the user double clicks the spinner while
|
||||
/// it contains zero.
|
||||
@ -135,35 +147,34 @@ void SpinBox::OnTimeout()
|
||||
bool SpinBox::eventFilter(QObject* o, QEvent* e)
|
||||
{
|
||||
QMouseEvent* me = dynamic_cast<QMouseEvent*>(e);
|
||||
auto settings = FractoriumSettings::DefInstance();
|
||||
|
||||
if (isEnabled() && me)
|
||||
{
|
||||
if (!settings->ToggleType() &&//Ensure double click toggles, not right click.
|
||||
if (!m_Settings->ToggleType() &&//Ensure double click toggles, not right click.
|
||||
me->type() == QMouseEvent::MouseButtonPress &&
|
||||
me->button() == Qt::RightButton)
|
||||
{
|
||||
m_MouseDownPoint = m_MouseMovePoint = me->pos();
|
||||
StartTimer();
|
||||
}
|
||||
else if (!settings->ToggleType() &&
|
||||
else if (!m_Settings->ToggleType() &&
|
||||
me->type() == QMouseEvent::MouseButtonRelease &&
|
||||
me->button() == Qt::RightButton)
|
||||
{
|
||||
StopTimer();
|
||||
m_MouseDownPoint = m_MouseMovePoint = me->pos();
|
||||
}
|
||||
else if (!settings->ToggleType() &&
|
||||
else if (!m_Settings->ToggleType() &&
|
||||
me->type() == QMouseEvent::MouseMove &&
|
||||
QGuiApplication::mouseButtons() & Qt::RightButton)
|
||||
{
|
||||
m_MouseMovePoint = me->pos();
|
||||
}
|
||||
else if (m_DoubleClick &&
|
||||
((!settings->ToggleType() && e->type() == QMouseEvent::MouseButtonDblClick && me->button() == Qt::LeftButton) ||
|
||||
(settings->ToggleType() && me->type() == QMouseEvent::MouseButtonRelease && me->button() == Qt::RightButton)))
|
||||
((!m_Settings->ToggleType() && e->type() == QMouseEvent::MouseButtonDblClick && me->button() == Qt::LeftButton) ||
|
||||
(m_Settings->ToggleType() && me->type() == QMouseEvent::MouseButtonRelease && me->button() == Qt::RightButton)))
|
||||
{
|
||||
if (IsNearZero(value()))
|
||||
if (IsClose(m_DoubleClickLowVal, value()))
|
||||
setValue(m_DoubleClickZero);
|
||||
else
|
||||
setValue(m_DoubleClickNonZero);
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
void SetValueStealth(int d);
|
||||
void SetValueStealth(size_t d);
|
||||
void DoubleClick(bool b);
|
||||
void DoubleClickLowVal(int val);
|
||||
void DoubleClickZero(int val);
|
||||
void DoubleClickNonZero(int val);
|
||||
void SmallStep(int step);
|
||||
@ -43,11 +44,13 @@ private:
|
||||
void StopTimer();
|
||||
|
||||
bool m_DoubleClick;
|
||||
int m_DoubleClickLowVal;
|
||||
int m_DoubleClickNonZero;
|
||||
int m_DoubleClickZero;
|
||||
int m_Step;
|
||||
int m_SmallStep;
|
||||
QPoint m_MouseDownPoint;
|
||||
QPoint m_MouseMovePoint;
|
||||
shared_ptr<FractoriumSettings> m_Settings;
|
||||
static QTimer s_Timer;
|
||||
};
|
||||
|
Reference in New Issue
Block a user