Remove ReadMe.txt from all project files.

Add Curves.h, and CurvesGraphicsView.h/cpp to support bezier color curves.
Add Curves member to Ember.
Add curves capability to EmberCL.
Remove some unused variables in the kernel created in RendererCL::CreateFinalAccumKernelString().
Use glm namespace for vec classes if GLM_VERSION >= 96, else use glm::detail.
As a result of using glm namespace, all instances of min and max had to be qualified with std::
Split ComputeCamera into that and ComputeQuality().
Reduce the amount of ComputeCamera() and MakeDmap() calls on each incremental iter that doesn't use temporal samples.
Fix clamping bug with DE filter widths.
Provide functions to return the kernels from RendererCL to assist with diagnostics and debugging.
Prevent extra newline in EmberRender when only rendering a single image.
Add the ability to delete an ember at a given index in EmberFile.
Allow deleting/focusing ember in library tab with delete and enter keys.
Reorder some code in Fractorium.h to match the tabs order.
Add and call ClearFinalImages() to clear buffers in controller to fix bug where previous CPU render would be shown for a split second when switching from OpenCL back to CPU.
Refactor ember library pointer syncing to a function SyncPointers().
Add the ability to save ember Xmls to an unique automatically generated name after the first time the user has specified a name.
This commit is contained in:
mfeemster
2015-03-21 15:27:37 -07:00
parent f334ce1704
commit 07592c9d78
67 changed files with 1585 additions and 263 deletions

View File

@ -58,7 +58,7 @@
</font>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Fractorium 0.4.1.8 Beta&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;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.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Matt Feemster&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;br/&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Fractorium 0.4.1.9 Beta&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;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.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Matt Feemster&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>

View File

@ -0,0 +1,213 @@
#include "FractoriumPch.h"
#include "CurvesGraphicsView.h"
/// <summary>
/// Construct the scene which will have a fixed rect.
/// Construct all points, pens and axes.
/// </summary>
/// <param name="parent">Pass to the parent</param>
CurvesGraphicsView::CurvesGraphicsView(QWidget* parent)
: QGraphicsView(parent)
{
m_Scene.setSceneRect(0, 0, 245, 245);
m_AllP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 0, 1, this);
m_AllP1->setBrush(QBrush(Qt::GlobalColor::black));
m_AllP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 0, 2, this);
m_AllP2->setBrush(QBrush(Qt::GlobalColor::black));
m_RedP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 1, 1, this);
m_RedP1->setBrush(QBrush(Qt::GlobalColor::red));
m_RedP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 1, 2, this);
m_RedP2->setBrush(QBrush(Qt::GlobalColor::red));
m_GrnP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 2, 1, this);
m_GrnP1->setBrush(QBrush(Qt::GlobalColor::green));
m_GrnP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 2, 2, this);
m_GrnP2->setBrush(QBrush(Qt::GlobalColor::green));
m_BluP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 3, 1, this);
m_BluP1->setBrush(QBrush(Qt::GlobalColor::blue));
m_BluP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 3, 2, this);
m_BluP2->setBrush(QBrush(Qt::GlobalColor::blue));
m_AxisPen = QPen(Qt::GlobalColor::white);
m_XLine = new QGraphicsLineItem();
m_XLine->setPen(m_AxisPen);
m_XLine->setZValue(0);
m_YLine = new QGraphicsLineItem();
m_YLine->setPen(m_AxisPen);
m_YLine->setZValue(0);
m_Scene.addItem(m_AllP1); m_Points[0].first = m_AllP1; m_AllP1->setZValue(2);
m_Scene.addItem(m_AllP2); m_Points[0].second = m_AllP2; m_AllP2->setZValue(2);
m_Scene.addItem(m_RedP1); m_Points[1].first = m_RedP1;
m_Scene.addItem(m_RedP2); m_Points[1].second = m_RedP2;
m_Scene.addItem(m_GrnP1); m_Points[2].first = m_GrnP1;
m_Scene.addItem(m_GrnP2); m_Points[2].second = m_GrnP2;
m_Scene.addItem(m_BluP1); m_Points[3].first = m_BluP1;
m_Scene.addItem(m_BluP2); m_Points[3].second = m_BluP2;
m_Scene.addItem(m_XLine);
m_Scene.addItem(m_YLine);
m_APen = QPen(Qt::GlobalColor::black); m_Pens[0] = &m_APen;
m_RPen = QPen(Qt::GlobalColor::red); m_Pens[1] = &m_RPen;
m_GPen = QPen(Qt::GlobalColor::green); m_Pens[2] = &m_GPen;
m_BPen = QPen(Qt::GlobalColor::blue); m_Pens[3] = &m_BPen;
m_APen.setWidth(2);
m_RPen.setWidth(2);
m_GPen.setWidth(2);
m_BPen.setWidth(2);
setScene(&m_Scene);
SetTop(CurveIndex::ALL);
//qDebug() << "Original scene rect before setting anything is: " << sceneRect();
m_OriginalRect = sceneRect();
show();
//qDebug() << "Original scene rect is: " << m_OriginalRect;
}
/// <summary>
/// Get the position of a given point within a given curve.
/// </summary>
/// <param name="curveIndex">The curve whose point value will be retrieved, 0-3.</param>
/// <param name="pointIndex">The point within the curve value will be retrieved, 1-2.</param>
/// <param name="point">The position of the point. X,Y will each be within 0-1.</param>
void CurvesGraphicsView::PointChanged(int curveIndex, int pointIndex, const QPointF& point)
{
double x = point.x() / width();
double y = (height() - point.y()) / height();
emit PointChangedSignal(curveIndex, pointIndex, QPointF(x, y));
}
/// <summary>
/// Get the position of a given point within a given curve.
/// </summary>
/// <param name="curveIndex">The curve whose point value will be retrieved, 0-3.</param>
/// <param name="pointIndex">The point within the curve whose value will be retrieved, 1-2.</param>
/// <returns>The position of the point. X,Y will each be within 0-1.</returns>
QPointF CurvesGraphicsView::Get(int curveIndex, int pointIndex)
{
if (curveIndex < 4)
{
EllipseItem* item = (pointIndex == 1) ? m_Points[curveIndex].first : m_Points[curveIndex].second;
return QPointF(item->pos().x() / width(), (height() - item->pos().y()) / height());
}
return QPointF();
}
/// <summary>
/// Set the position of a given point within a given curve.
/// </summary>
/// <param name="curveIndex">The curve whose point will be set, 0-3.</param>
/// <param name="pointIndex">The point within the curve which will be set, 1-2</param>
/// <param name="point">The position to set the point to. X,Y will each be within 0-1.</param>
void CurvesGraphicsView::Set(int curveIndex, int pointIndex, const QPointF& point)
{
if (curveIndex < 4)
{
if (pointIndex == 1)
m_Points[curveIndex].first->setPos(point.x() * width(), (1.0 - point.y()) * height());//Scale to scene dimensions, Y axis is flipped.
else
m_Points[curveIndex].second->setPos(point.x() * width(), (1.0 - point.y()) * height());
}
}
/// <summary>
/// Set the topmost curve but setting its Z value.
/// All other curves will get a value one less.
/// </summary>
/// <param name="curveIndex">The curve to set</param>
void CurvesGraphicsView::SetTop(CurveIndex curveIndex)
{
int index;
switch (curveIndex)
{
case CurveIndex::ALL:
index = 0;
break;
case CurveIndex::RED:
index = 1;
break;
case CurveIndex::GREEN:
index = 2;
break;
case CurveIndex::BLUE:
default:
index = 3;
}
for (int i = 0; i < 4; i++)
{
if (i == index)
{
m_Points[i].first->setZValue(2);
m_Points[i].second->setZValue(2);
}
else
{
m_Points[i].first->setZValue(1);
m_Points[i].second->setZValue(1);
}
}
}
/// <summary>
/// Overridden paint even which draws the points, axes and curves.
/// </summary>
/// <param name="e">Ignored</param>
void CurvesGraphicsView::paintEvent(QPaintEvent* e)
{
QGraphicsView::paintEvent(e);
int i;
QRectF rect = scene()->sceneRect();
double w2 = width() / 2;
double h2 = height() / 2;
//Draw axis lines.
m_XLine->setLine(QLineF(0, h2, width(), h2));
m_YLine->setLine(QLineF(w2, 0, w2, height()));
//This must be constructed every time and cannot be a member.
QPainter painter(viewport());
painter.setClipRect(rect);
painter.setRenderHint(QPainter::Antialiasing);
//Create 4 new paths. These must be constructed every time and cannot be members.
QPainterPath paths[4] =
{
QPainterPath(mapFromScene(rect.bottomLeft())),
QPainterPath(mapFromScene(rect.bottomLeft())),
QPainterPath(mapFromScene(rect.bottomLeft())),
QPainterPath(mapFromScene(rect.bottomLeft()))
};
//Draw paths for all but the topmost curve.
for (i = 0; i < 4; i++)
{
paths[i].cubicTo(m_Points[i].first->pos(), m_Points[i].second->pos(), mapFromScene(rect.topRight()));
if (m_Points[i].first->zValue() == 1)
{
painter.setPen(*m_Pens[i]);
painter.drawPath(paths[i]);
}
}
//Draw the topmost curve.
for (i = 0; i < 4; i++)
{
if (m_Points[i].first->zValue() == 2)
{
painter.setPen(*m_Pens[i]);
painter.drawPath(paths[i]);
break;
}
}
}

View File

@ -0,0 +1,150 @@
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// CurvesGraphicsView and EllipseItem classes.
/// </summary>
class EllipseItem;
/// <summary>
/// Enumeration used for setting values on a specific curve.
/// </summary>
enum CurveIndex
{
ALL,
RED,
GREEN,
BLUE
};
/// <summary>
/// Derivation to display points on bezier cuves for the user to drag.
/// Selection logic and updating is handled by the base class.
/// Changes here will affect the current ember and vice versa.
/// The points, axis lines and pens are kept as members and the curves
/// themselves are drawn on the fly during each paint event.
/// Pointers to these are kept in arrays to make manipulating them in loops easier.
/// Note that this must work off a fixed rect size, hence why the control is not resizeable.
/// </summary>
class CurvesGraphicsView : public QGraphicsView
{
Q_OBJECT
public:
CurvesGraphicsView(QWidget* parent = 0);
void PointChanged(int curveIndex, int pointIndex, const QPointF& point);
QPointF Get(int curveIndex, int pointIndex);
void Set(int curveIndex, int pointIndex, const QPointF& point);
void SetTop(CurveIndex curveIndex);
Q_SIGNALS:
void PointChangedSignal(int curveIndex, int pointIndex, const QPointF& point);
protected:
virtual void paintEvent(QPaintEvent* e) override;
QPen m_APen;
QPen m_RPen;
QPen m_GPen;
QPen m_BPen;
QPen m_AxisPen;
EllipseItem* m_AllP1;
EllipseItem* m_AllP2;
EllipseItem* m_RedP1;
EllipseItem* m_RedP2;
EllipseItem* m_GrnP1;
EllipseItem* m_GrnP2;
EllipseItem* m_BluP1;
EllipseItem* m_BluP2;
QGraphicsLineItem* m_XLine;
QGraphicsLineItem* m_YLine;
QPen* m_Pens[4];
QGraphicsScene m_Scene;
QRectF m_OriginalRect;
std::pair<EllipseItem*, EllipseItem*> m_Points[4];
};
/// <summary>
/// Derivation for draggable points needed to trigger an event whenever the item is changed.
/// Custom drawing is also done to omit drawing a selection rectangle.
/// </summary>
class EllipseItem : public QGraphicsEllipseItem
{
public:
/// <summary>
/// Construct the point and specify the curve index it's part of, as well as the
/// point index within the curve.
/// </summary>
/// <param name="rect">Pass to the parent</param>
/// <param name="curveIndex">The curve's index this point is a part of, 0-3.</param>
/// <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)
: QGraphicsEllipseItem(rect, parent)
{
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsMovable);
setPen(Qt::NoPen);
m_CurveIndex = curveIndex;
m_PointIndex = pointIndex;
m_ViewParent = viewParent;
}
/// <summary>
/// Index properties, getters only.
/// </summary>
int CurveIndex() const { return m_CurveIndex; }
int PointIndex() const { return m_PointIndex; }
protected:
/// <summary>
/// Overridden paint event to disable the selection rectangle.
/// </summary>
/// <param name="painter">Unused and just passed to QGraphicsEllipseItem::paint()</param>
/// <param name="option">Drawing options used which will have the QStyle::State_Selected flag unset</param>
/// <param name="widget">Unused and just passed to QGraphicsEllipseItem::paint()</param>
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
QStyleOptionGraphicsItem myOption(*option);
myOption.state &= ~QStyle::State_Selected;
QGraphicsEllipseItem::paint(painter, &myOption, widget);
}
/// <summary>
/// Overridden itemChange event to notify the parent control that it has moved.
/// Movement is also restriced to the scene rect.
/// </summary>
/// <param name="change">Action is only taken if this value equals ItemPositionChange</param>
/// <param name="value">The new position. This will be clamped to the scene rect.</param>
/// <returns>The new position</returns>
virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
{
if (change == ItemPositionChange && scene())
{
//Value is the new position.
QPointF newPos = value.toPointF();
QRectF rect = scene()->sceneRect();
if (!rect.contains(newPos))
{
//Keep the item inside the scene rect.
newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
}
m_ViewParent->PointChanged(m_CurveIndex, m_PointIndex, newPos);
return newPos;
}
return QGraphicsEllipseItem::itemChange(change, value);
}
int m_CurveIndex;
int m_PointIndex;
CurvesGraphicsView* m_ViewParent;
};

View File

@ -84,6 +84,23 @@ public:
return m_Embers.size();
}
/// <summary>
/// Delete the ember at the given index.
/// Will not delete anything if the size is already 1.
/// </summary>
/// <param name="index">The index of the ember to delete</param>
/// <returns>True if successfully deleted, else false.</returns>
bool Delete(size_t index)
{
if (Size() > 1 && index < Size())
{
m_Embers.erase(m_Embers.begin() + index);
return true;
}
else
return false;
}
/// <summary>
/// Ensure all ember names are unique.
/// </summary>
@ -116,6 +133,7 @@ public:
/// <summary>
/// Ensures a given input filename is unique by appending a count to the end.
/// </summary>
/// <param name="filename">The filename to ensure is unique</param>
/// <returns>The passed in name if it was unique, else a uniquely made name.</returns>
static QString UniqueFilename(const QString& filename)
{

View File

@ -139,7 +139,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
QSize s = size();
int desktopHeight = qApp->desktop()->availableGeometry().height();
s.setHeight(min(s.height(), int(double(desktopHeight * 0.90))));
s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90))));
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry()));
QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox);

View File

@ -115,8 +115,8 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
m_PreviewEmber = *m_Ember;
m_PreviewEmber.m_Quality = 100;
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_FinalRasW = max<size_t>(1, min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasW)));//Ensure neither is zero.
m_PreviewEmber.m_FinalRasH = max<size_t>(1, min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasH)));
m_PreviewEmber.m_FinalRasW = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasW)));//Ensure neither is zero.
m_PreviewEmber.m_FinalRasH = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasH)));
m_PreviewEmber.m_PixelsPerUnit = scalePercentage * m_Ember->m_PixelsPerUnit;
m_FinalPreviewRenderer->EarlyClip(m_FinalRenderDialog->EarlyClip());
@ -597,6 +597,7 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
m_Renderer->CreateTemporalFilter(b);
m_Renderer->NumChannels(channels);
m_Renderer->ComputeBounds();
m_Renderer->ComputeQuality();
m_Renderer->ComputeCamera();
CancelPreviewRender();
m_FinalPreviewRenderFunc();
@ -741,8 +742,8 @@ void FinalRenderEmberController<T>::SyncGuiToEmber(Ember<T>& ember, size_t width
h = ember.m_OrigFinalRasH * hScale;
}
w = max<size_t>(w, 10);
h = max<size_t>(h, 10);
w = std::max<size_t>(w, 10);
h = std::max<size_t>(h, 10);
ember.SetSizeAndAdjustScale(w, h, false, m_FinalRenderDialog->Scale());
ember.m_Quality = m_FinalRenderDialog->m_QualitySpin->value();

View File

@ -264,8 +264,9 @@ void Fractorium::dockLocationChanged(Qt::DockWidgetArea area)
/// </summary>
/// <summary>
/// Event filter for taking special action on dock widget resize events,
/// which in turn trigger GLParentScrollArea events.
/// Event filter for taking special action on:
/// Dock widget resize events, which in turn trigger GLParentScrollArea events.
/// Library tree key events, specifically delete.
/// </summary>
/// <param name="o">The object</param>
/// <param name="e">The eevent</param>
@ -278,6 +279,19 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e)
m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height());
//qDebug() << "scroll area resized";
}
else if (o == ui.LibraryTree)
{
if (QKeyEvent* ke = dynamic_cast<QKeyEvent*>(e))
{
if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease)
{
auto p = GetCurrentEmberIndex();
if (ui.LibraryTree->topLevelItem(0)->childCount() > 1 && p.second)
OnDelete(p);
}
}
}
return QMainWindow::eventFilter(o, e);
}

View File

@ -10,6 +10,7 @@
#include "FinalRenderDialog.h"
#include "OptionsDialog.h"
#include "AboutDialog.h"
#include "CurvesGraphicsView.h"
/// <summary>
/// Fractorium class.
@ -140,6 +141,11 @@ public slots:
void OnSaveEntireFileAsXmlButtonClicked(bool checked);
void OnSaveCurrentToOpenedFileButtonClicked(bool checked);
//Library.
void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col);
void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col);
void OnDelete(const pair<size_t, QTreeWidgetItem*>& p);
//Params.
void OnBrightnessChanged(double d);//Color.
void OnGammaChanged(double d);
@ -222,7 +228,13 @@ public slots:
void OnXformDirectColorChanged(double d);
void OnSoloXformCheckBoxStateChanged(int state);
void OnXformRefPaletteResized(int logicalIndex, int oldSize, int newSize);
void OnResetCurvesButtonClicked(bool checked);
void OnCurvesPointChanged(int curveIndex, int pointIndex, const QPointF& point);
void OnCurvesAllRadioButtonToggled(bool checked);
void OnCurvesRedRadioButtonToggled(bool checked);
void OnCurvesGreenRadioButtonToggled(bool checked);
void OnCurvesBlueRadioButtonToggled(bool checked);
//Xforms Variations.
void OnVariationSpinBoxValueChanged(double d);
void OnTreeHeaderSectionClicked(int);
@ -242,10 +254,6 @@ public slots:
void OnPaletteRandomSelectButtonClicked(bool checked);
void OnPaletteRandomAdjustButtonClicked(bool checked);
//Library.
void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col);
void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col);
//Rendering/progress.
void StartRenderTimer();
void IdleTimer();
@ -287,6 +295,9 @@ private:
//Embers.
bool HaveFinal();
//Library.
pair<size_t, QTreeWidgetItem*> GetCurrentEmberIndex();
//Params.
//Xforms.
@ -304,8 +315,6 @@ private:
//Palette.
void ResetPaletteControls();
//Library.
//Info.
void UpdateHistogramBounds();
void ErrorReportToQTextEdit(const vector<string>& errors, QTextEdit* textEdit, bool clear = true);

Binary file not shown.

View File

@ -353,7 +353,7 @@
<enum>QTabWidget::Triangular</enum>
</property>
<property name="currentIndex">
<number>2</number>
<number>0</number>
</property>
<property name="usesScrollButtons">
<bool>true</bool>
@ -420,7 +420,7 @@
<item row="1" column="0">
<widget class="QTreeWidget" name="LibraryTree">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
<enum>Qt::WheelFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
@ -504,8 +504,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>73</width>
<height>837</height>
<width>259</width>
<height>852</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
@ -2285,7 +2285,7 @@ SpinBox
<enum>QTabWidget::Triangular</enum>
</property>
<property name="currentIndex">
<number>1</number>
<number>2</number>
</property>
<widget class="QWidget" name="ColorTab">
<property name="sizePolicy">
@ -2775,6 +2775,134 @@ SpinBox
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="CurvesGraphicsView" name="CurvesView">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>245</width>
<height>245</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>245</width>
<height>245</height>
</size>
</property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="backgroundBrush">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>219</red>
<green>219</green>
<blue>219</blue>
</color>
</brush>
</property>
<property name="sceneRect">
<rectf>
<x>0.000000000000000</x>
<y>0.000000000000000</y>
<width>245.000000000000000</width>
<height>245.000000000000000</height>
</rectf>
</property>
<property name="resizeAnchor">
<enum>QGraphicsView::NoAnchor</enum>
</property>
<property name="viewportUpdateMode">
<enum>QGraphicsView::FullViewportUpdate</enum>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Curve</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QRadioButton" name="CurvesAllRadio">
<property name="text">
<string>All</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="CurvesRedRadio">
<property name="text">
<string>Red</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="CurvesGreenRadio">
<property name="text">
<string>Green</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="CurvesBlueRadio">
<property name="text">
<string>Blue</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="ResetCurvesButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Reset Curves</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="AffineTab">
@ -2825,8 +2953,8 @@ SpinBox
<rect>
<x>0</x>
<y>0</y>
<width>245</width>
<height>747</height>
<width>118</width>
<height>618</height>
</rect>
</property>
<property name="autoFillBackground">
@ -5048,8 +5176,8 @@ SpinBox
<rect>
<x>0</x>
<y>0</y>
<width>73</width>
<height>454</height>
<width>259</width>
<height>853</height>
</rect>
</property>
<property name="sizePolicy">
@ -5855,6 +5983,11 @@ SpinBox
<header>GLWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>CurvesGraphicsView</class>
<extends>QGraphicsView</extends>
<header>curvesgraphicsview.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>DockWidget</tabstop>

View File

@ -95,7 +95,7 @@ FractoriumEmberController<T>::FractoriumEmberController(Fractorium* fractorium)
m_PreviewRun = true;
m_PreviewRunning = true;
m_PreviewRenderer->ThreadCount(max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
m_PreviewRenderer->ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
if (QTreeWidgetItem* top = tree->topLevelItem(0))
@ -208,7 +208,7 @@ void FractoriumEmberController<T>::SetEmber(size_t index)
/// Wrapper to call a function, then optionally add the requested action to the rendering queue.
/// </summary>
/// <param name="func">The function to call</param>
/// <param name="updateRender">True to update renderer, else false. Default: false.</param>
/// <param name="updateRender">True to update renderer, else false. Default: true.</param>
/// <param name="action">The action to add to the rendering queue. Default: FULL_RENDER.</param>
template <typename T>
void FractoriumEmberController<T>::Update(std::function<void (void)> func, bool updateRender, eProcessAction action)

View File

@ -55,7 +55,7 @@ public:
virtual void CopyTempPalette(Palette<double>& palette) { }
#endif
virtual void SetEmber(size_t index) { }
virtual void Clear() { }
//virtual void Clear() { }
virtual void AddXform() { }
virtual void DuplicateXform() { }
virtual void ClearCurrentXform() { }
@ -98,6 +98,17 @@ public:
//Toolbar.
//Library.
virtual void SyncNames() { }
virtual void SyncPointers() { }
virtual void FillLibraryTree(int selectIndex = -1) { }
virtual void UpdateLibraryTree() { }
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) { }
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { }
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { }
virtual void StopPreviewRender() { }
virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) { }
//Params.
virtual void SetCenter(double x, double y) { }
virtual void FillParamTablesAndPalette() { }
@ -156,6 +167,8 @@ public:
virtual void XformColorSpeedChanged(double d) { }
virtual void XformOpacityChanged(double d) { }
virtual void XformDirectColorChanged(double d) { }
virtual void ClearColorCurves() { }
virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) { }//need to put this in a different section because it's not xform specific.//TODO
void SetPaletteRefTable(QPixmap* pixmap);
//Xforms Variations.
@ -177,15 +190,6 @@ public:
virtual QRgb GetQRgbFromPaletteIndex(uint i) { return QRgb(); }
virtual void PaletteCellClicked(int row, int col) { }
//Library.
virtual void SyncNames() { }
virtual void FillLibraryTree(int selectIndex = -1) { }
virtual void UpdateLibraryTree() { }
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) { }
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { }
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { }
virtual void StopPreviewRender() { }
//Info.
//Rendering/progress.
@ -198,6 +202,7 @@ public:
void StartRenderTimer();
void DelayedStartRenderTimer();
void StopRenderTimer(bool wait);
void ClearFinalImages();
void Shutdown();
void UpdateRender(eProcessAction action = FULL_RENDER);
void DeleteRenderer();
@ -271,7 +276,7 @@ public:
virtual void CopyTempPalette(Palette<double>& palette) override;
#endif
virtual void SetEmber(size_t index) override;
virtual void Clear() override { }
//virtual void Clear() override { }
virtual void AddXform() override;
virtual void DuplicateXform() override;
virtual void ClearCurrentXform() override;
@ -317,6 +322,17 @@ public:
//Toolbar.
//Library.
virtual void SyncNames() override;
virtual void SyncPointers() override;
virtual void FillLibraryTree(int selectIndex = -1) override;
virtual void UpdateLibraryTree() override;
virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) override;
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) override;
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) override;
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override;
virtual void StopPreviewRender() override;
//Params.
virtual void SetCenter(double x, double y) override;
virtual void FillParamTablesAndPalette() override;
@ -378,6 +394,8 @@ public:
virtual void XformColorSpeedChanged(double d) override;
virtual void XformOpacityChanged(double d) override;
virtual void XformDirectColorChanged(double d) override;
virtual void ClearColorCurves() override;
virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) override;
void FillColorWithXform(Xform<T>* xform);
//Xforms Variations.
@ -400,15 +418,6 @@ public:
virtual QRgb GetQRgbFromPaletteIndex(uint i) override { return QRgb(); }
virtual void PaletteCellClicked(int row, int col) override;
//Library.
virtual void SyncNames() override;
virtual void FillLibraryTree(int selectIndex = -1) override;
virtual void UpdateLibraryTree() override;
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) override;
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) override;
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override;
virtual void StopPreviewRender() override;
//Info.
//Rendering/progress.
@ -433,6 +442,7 @@ private:
//Xforms Color.
void SetCurrentXformColorIndex(double d);
void FillCurvesControl();
//Palette.
void UpdateAdjustedPaletteGUI(Palette<T>& palette);

View File

@ -8,6 +8,35 @@ void Fractorium::InitLibraryUI()
{
connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
ui.LibraryTree->installEventFilter(this);//Needed for keypress events other than enter.
}
/// <summary>
/// Get the index of the currently selected ember in the library tree.
/// </summary>
/// <returns>A pair containing the index of the item clicked and a pointer to the item</param>
pair<size_t, QTreeWidgetItem*> Fractorium::GetCurrentEmberIndex()
{
size_t index = 0;
QTreeWidgetItem* item = nullptr;
QTreeWidget* tree = ui.LibraryTree;
if (QTreeWidgetItem* top = tree->topLevelItem(0))
{
for (int i = 0; i < top->childCount(); i++)//Iterate through all of the children, which will represent the open embers.
{
item = top->child(index);
if (item && !item->isSelected())
index++;
else
break;
}
}
return pair<size_t, QTreeWidgetItem*>(index, item);
}
/// <summary>
@ -30,11 +59,10 @@ void FractoriumEmberController<T>::SyncNames()
{
EmberTreeWidgetItem<T>* item;
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
QTreeWidgetItem* top = tree->topLevelItem(0);
tree->blockSignals(true);
if (top)
if (QTreeWidgetItem* top = tree->topLevelItem(0))
{
for (int i = 0; i < top->childCount(); i++)//Iterate through all of the children, which will represent the open embers.
{
@ -46,6 +74,31 @@ void FractoriumEmberController<T>::SyncNames()
tree->blockSignals(false);
}
/// <summary>
/// Set all libary tree entries to point to the underlying ember they represent.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::SyncPointers()
{
EmberTreeWidgetItem<T>* item;
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
tree->blockSignals(true);
if (QTreeWidgetItem* top = tree->topLevelItem(0))
{
size_t childCount = top->childCount();
for (int i = 0; i < childCount; i++)//Iterate through all of the children, which will represent the open embers.
{
if ((item = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i))) && i < m_EmberFile.Size())//Cast the child widget to the EmberTreeWidgetItem type.
item->SetEmberPointer(&m_EmberFile.m_Embers[i]);
}
}
tree->blockSignals(false);
}
/// <summary>
/// Fill the library tree with the names of the embers in the
/// currently opened file.
@ -135,12 +188,7 @@ void FractoriumEmberController<T>::UpdateLibraryTree()
//When adding elements to the vector, they may have been reshuffled which will have invalidated
//the pointers contained in the EmberTreeWidgetItems. So reassign all pointers here.
for (i = 0; i < m_EmberFile.Size(); i++)
{
if (EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
emberItem->SetEmberPointer(&m_EmberFile.m_Embers[i]);
}
SyncPointers();
tree->blockSignals(false);
RenderPreviews(childCount, m_EmberFile.Size());
}
@ -221,6 +269,47 @@ void FractoriumEmberController<T>::EmberTreeItemDoubleClicked(QTreeWidgetItem* i
void Fractorium::OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { m_Controller->EmberTreeItemDoubleClicked(item, col); }
/// <summary>
/// Delete the currently selected item in the tree.
/// Note this is not necessarilly the current ember, it's just the item
/// in the tree that is selected.
/// </summary>
/// <param name="p">A pair containing the index of the item clicked and a pointer to the item</param>
template <typename T>
void FractoriumEmberController<T>::Delete(const pair<size_t, QTreeWidgetItem*>& p)
{
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
tree->blockSignals(true);
if (m_EmberFile.Delete(p.first))
{
delete p.second;
SyncPointers();
}
tree->blockSignals(false);
//If there is now only one item left and it wasn't selected, select it.
if (QTreeWidgetItem* top = tree->topLevelItem(0))
{
if (top->childCount() == 1)
if (auto item = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(0)))
if (item->GetEmber()->m_Name != m_Ember.m_Name)
EmberTreeItemDoubleClicked(top->child(0), 0);
}
}
/// <summary>
/// Called when the user presses and releases the delete key while the library tree has the focus,
/// and an item is selected.
/// </summary>
/// <param name="p">A pair containing the index of the item clicked and a pointer to the item</param>
void Fractorium::OnDelete(const pair<size_t, QTreeWidgetItem*>& p)
{
m_Controller->Delete(p);
}
/// <summary>
/// Stop the preview renderer if it's already running.
/// Clear all of the existing preview images, then start the preview rendering thread.

View File

@ -252,7 +252,11 @@ void FractoriumEmberController<T>::SaveCurrentAsXml()
QString filename;
FractoriumSettings* s = m_Fractorium->m_Settings;
if (QFile::exists(m_LastSaveCurrent))
if (s->SaveAutoUnique() && m_LastSaveCurrent != "")
{
filename = EmberFile<T>::UniqueFilename(m_LastSaveCurrent);
}
else if (QFile::exists(m_LastSaveCurrent))
{
filename = m_LastSaveCurrent;
}
@ -281,7 +285,9 @@ void FractoriumEmberController<T>::SaveCurrentAsXml()
if (writer.Save(filename.toStdString().c_str(), ember, 0, true, false, true))
{
s->SaveFolder(fileInfo.canonicalPath());
m_LastSaveCurrent = filename;
if (!s->SaveAutoUnique() || m_LastSaveCurrent == "")//Only save filename on first time through when doing auto unique names.
m_LastSaveCurrent = filename;
}
else
m_Fractorium->ShowCritical("Save Failed", "Could not save file, try saving to a different folder.");
@ -301,7 +307,9 @@ void FractoriumEmberController<T>::SaveEntireFileAsXml()
QString filename;
FractoriumSettings* s = m_Fractorium->m_Settings;
if (QFile::exists(m_LastSaveAll))
if (s->SaveAutoUnique() && m_LastSaveAll != "")
filename = EmberFile<T>::UniqueFilename(m_LastSaveAll);
else if (QFile::exists(m_LastSaveAll))
filename = m_LastSaveAll;
else
filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename);
@ -320,7 +328,9 @@ void FractoriumEmberController<T>::SaveEntireFileAsXml()
if (writer.Save(filename.toStdString().c_str(), emberFile.m_Embers, 0, true, false, true))
{
m_LastSaveAll = filename;
if (!s->SaveAutoUnique() || m_LastSaveAll == "")//Only save filename on first time through when doing auto unique names.
m_LastSaveAll = filename;
s->SaveFolder(fileInfo.canonicalPath());
}
else
@ -398,7 +408,7 @@ void FractoriumEmberController<T>::Undo()
int index = m_Ember.GetTotalXformIndex(CurrentXform());
m_LastEditWasUndoRedo = true;
m_UndoIndex = max(0u, m_UndoIndex - 1u);
m_UndoIndex = std::max(0u, m_UndoIndex - 1u);
SetEmber(m_UndoList[m_UndoIndex], true);
m_EditState = UNDO_REDO;
@ -423,7 +433,7 @@ void FractoriumEmberController<T>::Redo()
int index = m_Ember.GetTotalXformIndex(CurrentXform());
m_LastEditWasUndoRedo = true;
m_UndoIndex = min<uint>(m_UndoIndex + 1, m_UndoList.size() - 1);
m_UndoIndex = std::min<uint>(m_UndoIndex + 1, m_UndoList.size() - 1);
SetEmber(m_UndoList[m_UndoIndex], true);
m_EditState = UNDO_REDO;

View File

@ -26,9 +26,11 @@
#include <QLineEdit>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QPainterPath>
#include <QPushButton>
#include <QComboBox>
#include <QColorDialog>
#include <QGraphicsView>
#include <QTreeWidget>
#include <QWheelEvent>
#include <QItemDelegate>

View File

@ -67,11 +67,22 @@ void FractoriumEmberControllerBase::StopRenderTimer(bool wait)
void FractoriumEmberControllerBase::Shutdown()
{
StopRenderTimer(true);
ClearFinalImages();
while(m_Fractorium->ui.GLDisplay->Drawing())
QApplication::processEvents();
}
/// <summary>
/// Clear the output image buffers.
/// </summary>
void FractoriumEmberControllerBase::ClearFinalImages()
{
Memset(m_FinalImage[0]);
Memset(m_FinalImage[1]);
//Unsure if we should also call RendererCL::ClearFinal() as well. At the moment it seems unnecessary.
}
/// <summary>
/// Update the state of the renderer.
/// Upon changing values, some intelligence is used to avoid blindly restarting the
@ -442,7 +453,11 @@ bool FractoriumEmberController<T>::Render()
//Uncomment for debugging kernel build and execution errors.
//m_Fractorium->ui.InfoRenderingTextEdit->setText(QString::fromStdString(m_Fractorium->m_Wrapper.DumpInfo()));
//if (rendererCL)
// m_Fractorium->ui.InfoRenderingTextEdit->setText(QString::fromStdString(rendererCL->IterKernel()));
//{
// string s = "OpenCL Kernels: \r\n" + rendererCL->IterKernel() + "\r\n" + rendererCL->DEKernel() + "\r\n" + rendererCL->FinalAccumKernel();
//
// QMetaObject::invokeMethod(m_Fractorium->ui.InfoRenderingTextEdit, "setText", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(s)));
//}
}
}
else//Something went very wrong, show error report.
@ -460,12 +475,16 @@ bool FractoriumEmberController<T>::Render()
m_Rendering = false;
StopRenderTimer(true);
m_Fractorium->m_RenderStatusLabel->setText("Rendering failed 3 or more times, stopping all rendering, see info tab. Try changing renderer types.");
Memset(m_FinalImage[m_FinalImageIndex]);
if (rendererCL)
rendererCL->ClearFinal();
ClearFinalImages();
m_GLController->ClearWindow();
if (rendererCL)
{
//string s = "OpenCL Kernels: \r\n" + rendererCL->IterKernel() + "\r\n" + rendererCL->DEKernel() + "\r\n" + rendererCL->FinalAccumKernel();
rendererCL->ClearFinal();
//QMetaObject::invokeMethod(m_Fractorium->ui.InfoRenderingTextEdit, "setText", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(s)));
}
}
}
}

View File

@ -39,7 +39,7 @@ void FractoriumSettings::EnsureDefaults()
XmlSupersample(2);
if (ThreadCount() == 0 || ThreadCount() > Timing::ProcessorCount())
ThreadCount(max(1u, Timing::ProcessorCount() - 1));//Default to one less to keep the UI responsive for first time users.
ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Default to one less to keep the UI responsive for first time users.
if (FinalThreadCount() == 0 || FinalThreadCount() > Timing::ProcessorCount())
FinalThreadCount(Timing::ProcessorCount());
@ -172,8 +172,8 @@ void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT,
uint FractoriumSettings::FinalScale() { return value(FINALSCALE).toUInt(); }
void FractoriumSettings::FinalScale(uint i) { setValue(FINALSCALE, i); }
QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); }
void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); }
QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); }
void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); }
uint FractoriumSettings::FinalPlatformIndex() { return value(FINALPLATFORMINDEX).toUInt(); }
void FractoriumSettings::FinalPlatformIndex(uint i) { setValue(FINALPLATFORMINDEX, i); }
@ -239,3 +239,6 @@ void FractoriumSettings::OpenImageExt(const QString& s) { setValue(OPENIMAGEE
QString FractoriumSettings::SaveImageExt() { return value(SAVEIMAGEEXT).toString(); }
void FractoriumSettings::SaveImageExt(const QString& s) { setValue(SAVEIMAGEEXT, s); }
bool FractoriumSettings::SaveAutoUnique() { return value(AUTOUNIQUE).toBool(); }
void FractoriumSettings::SaveAutoUnique(bool b) { setValue(AUTOUNIQUE, b); }

View File

@ -52,6 +52,7 @@
#define SAVEXMLEXT "file/savexmlext"
#define OPENIMAGEEXT "file/openimageext"
#define SAVEIMAGEEXT "file/saveimageext"
#define AUTOUNIQUE "file/autounique"
#define IDENTITYID "identity/id"
#define IDENTITYURL "identity/url"
@ -189,6 +190,9 @@ public:
QString SaveImageExt();
void SaveImageExt(const QString& s);
bool SaveAutoUnique();
void SaveAutoUnique(bool b);
QString Id();
void Id(const QString& s);

View File

@ -25,8 +25,15 @@ void Fractorium::InitXformsColorUI()
m_XformColorSpeedSpin->setDecimals(3);
m_XformOpacitySpin->setDecimals(3);
m_XformDirectColorSpin->setDecimals(3);
connect(ui.XformColorScroll, SIGNAL(valueChanged(int)), this, SLOT(OnXformScrollColorIndexChanged(int)), Qt::QueuedConnection);
connect(ui.SoloXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSoloXformCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.XformColorScroll, SIGNAL(valueChanged(int)), this, SLOT(OnXformScrollColorIndexChanged(int)), Qt::QueuedConnection);
connect(ui.SoloXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSoloXformCheckBoxStateChanged(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>
@ -144,6 +151,55 @@ void Fractorium::OnXformRefPaletteResized(int logicalIndex, int oldSize, int new
m_Controller->SetPaletteRefTable(nullptr);
}
/// <summary>
/// Reset the color curve values in the current ember to their default state and also update the curves control.
/// Called when ResetCurvesButton is clicked.
/// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ClearColorCurves()
{
Update([&]
{
m_Ember.m_Curves.Init();
}, true, m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY);
FillCurvesControl();
}
void Fractorium::OnResetCurvesButtonClicked(bool checked) { m_Controller->ClearColorCurves(); }
/// <summary>
/// Set the coordinate of the curve point.
/// Called when the position of any of the points in the curves editor is is changed.
/// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip.
/// </summary>
/// <param name="curveIndex">The curve index, 0-1/</param>
/// <param name="pointIndex">The point index within the selected curve, 1-2.</param>
/// <param name="point">The new coordinate of the point in terms of the curves control rect.</param>
template <typename T>
void FractoriumEmberController<T>::ColorCurveChanged(int curveIndex, int pointIndex, const QPointF& point)
{
Update([&]
{
m_Ember.m_Curves.m_Points[curveIndex][pointIndex].x = point.x();
m_Ember.m_Curves.m_Points[curveIndex][pointIndex].y = point.y();
}, true, m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY);
}
void Fractorium::OnCurvesPointChanged(int curveIndex, int pointIndex, const QPointF& point) { m_Controller->ColorCurveChanged(curveIndex, pointIndex, point); }
/// <summary>
/// Set the top most points in the curves control, which makes it easier to
/// 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>
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); }
void Fractorium::OnCurvesBlueRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::BLUE); }
/// <summary>
/// Set the current xform color index spinner to the current xform's color index.
/// Set the color cell in the palette ref table.
@ -168,6 +224,28 @@ void FractoriumEmberController<T>::SetCurrentXformColorIndex(double d)
}, false);
}
/// <summary>
/// Set the points in the curves control to the values of the curve points in the current ember.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillCurvesControl()
{
m_Fractorium->ui.CurvesView->blockSignals(true);
for (int i = 0; i < 4; i++)
{
for (int j = 1; j < 3; j++)//Only do middle points.
{
QPointF point(m_Ember.m_Curves.m_Points[i][j].x, m_Ember.m_Curves.m_Points[i][j].y);
m_Fractorium->ui.CurvesView->Set(i, j, point);
}
}
m_Fractorium->ui.CurvesView->blockSignals(false);
m_Fractorium->ui.CurvesView->update();
}
/// <summary>
/// Set the color index, speed and opacity spinners with the values of the current xform.
/// Set the cells of the palette ref table as well.
@ -180,6 +258,7 @@ void FractoriumEmberController<T>::FillColorWithXform(Xform<T>* xform)
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(xform->m_ColorSpeed);
m_Fractorium->m_XformOpacitySpin->SetValueStealth(xform->m_Opacity);
m_Fractorium->m_XformDirectColorSpin->SetValueStealth(xform->m_DirectColor);
FillCurvesControl();
m_Fractorium->OnXformColorIndexChanged(xform->m_ColorX, false);//Had to call stealth before to avoid doing an update, now manually update related controls, still without doing an update.
}

View File

@ -203,7 +203,7 @@ void GLEmberController<double>::QueryVMP()
/// by an m4<float>.
/// </summary>
template <>
void GLEmberController<float>::MultMatrix(glm::detail::tmat4x4<float, glm::defaultp>& mat)
void GLEmberController<float>::MultMatrix(tmat4x4<float, glm::defaultp>& mat)
{
m_GL->glMultMatrixf(glm::value_ptr(mat));
}
@ -214,7 +214,7 @@ void GLEmberController<float>::MultMatrix(glm::detail::tmat4x4<float, glm::defau
/// by an m4<double>.
/// </summary>
template <>
void GLEmberController<double>::MultMatrix(glm::detail::tmat4x4<double, glm::defaultp>& mat)
void GLEmberController<double>::MultMatrix(tmat4x4<double, glm::defaultp>& mat)
{
m_GL->glMultMatrixd(glm::value_ptr(mat));
}

View File

@ -855,7 +855,7 @@ void GLWidget::DrawGrid()
RendererBase* renderer = m_Fractorium->m_Controller->Renderer();
float unitX = fabs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f;
float unitY = fabs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f;
float rad = max(unitX, unitY);
float rad = std::max(unitX, unitY);
float xLow = floor(-unitX);
float xHigh = ceil(unitX);
float yLow = floor(-unitY);

View File

@ -88,6 +88,7 @@ FractoriumOptionsDialog::FractoriumOptionsDialog(FractoriumSettings* settings, Q
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
m_XmlQualitySpin->setValue(m_Settings->XmlQuality());
m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample());
ui.AutoUniqueCheckBox->setChecked(m_Settings->SaveAutoUnique());
OnOpenCLCheckBoxStateChanged(ui.OpenCLCheckBox->isChecked());
}
@ -102,6 +103,7 @@ bool FractoriumOptionsDialog::Transparency() { return ui.TransparencyCheckBox->i
bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); }
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
uint FractoriumOptionsDialog::PlatformIndex() { return ui.PlatformCombo->currentIndex(); }
uint FractoriumOptionsDialog::DeviceIndex() { return ui.DeviceCombo->currentIndex(); }
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
@ -162,6 +164,7 @@ void FractoriumOptionsDialog::accept()
m_Settings->XmlTemporalSamples(m_XmlTemporalSamplesSpin->value());
m_Settings->XmlQuality(m_XmlQualitySpin->value());
m_Settings->XmlSupersample(m_XmlSupersampleSpin->value());
m_Settings->SaveAutoUnique(AutoUnique());
//Identity.
m_Settings->Id(m_IdEdit->text());
@ -196,7 +199,8 @@ void FractoriumOptionsDialog::reject()
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
m_XmlQualitySpin->setValue(m_Settings->XmlQuality());
m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample());
ui.AutoUniqueCheckBox->setChecked(m_Settings->SaveAutoUnique());
//Identity.
m_IdEdit->setText(m_Settings->Id());
m_UrlEdit->setText(m_Settings->Url());

View File

@ -40,6 +40,7 @@ private:
bool OpenCL();
bool Double();
bool ShowAllXforms();
bool AutoUnique();
uint PlatformIndex();
uint DeviceIndex();
uint ThreadCount();

View File

@ -486,6 +486,13 @@ in interactive mode for each mouse movement</string>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="AutoUniqueCheckBox">
<property name="text">
<string>Auto Unique Filenames</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="OptionsIdentityTab">

View File

@ -77,7 +77,7 @@ void SpinBox::DoubleClickNonZero(int val)
/// <param name="step">The small step to use for scrolling while the shift key is down</param>
void SpinBox::SmallStep(int step)
{
m_SmallStep = min(1, step);
m_SmallStep = std::min(1, step);
}
/// <summary>