Initial source commit
Initial source commit
14
Source/Fractorium/AboutDialog.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "AboutDialog.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a parent widget and passes it to the base, then
|
||||
/// sets up the GUI.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget. Default: NULL.</param>
|
||||
/// <param name="f">The window flags. Default: 0.</param>
|
||||
FractoriumAboutDialog::FractoriumAboutDialog(QWidget* parent, Qt::WindowFlags f)
|
||||
: QDialog(parent, f)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
}
|
22
Source/Fractorium/AboutDialog.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_AboutDialog.h"
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumAboutDialog class.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// The about dialog displays several group boxes showing the
|
||||
/// code and icons used in this project and their respective authors
|
||||
/// and licenses. It performs no other function.
|
||||
/// </summary>
|
||||
class FractoriumAboutDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FractoriumAboutDialog(QWidget* parent = 0, Qt::WindowFlags f = 0);
|
||||
|
||||
private:
|
||||
Ui::AboutDialog ui;
|
||||
};
|
199
Source/Fractorium/AboutDialog.ui
Normal file
@ -0,0 +1,199 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AboutDialog</class>
|
||||
<widget class="QDialog" name="AboutDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>488</width>
|
||||
<height>580</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>488</width>
|
||||
<height>580</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>488</width>
|
||||
<height>580</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><br/><span style=" font-size:12pt;">Fractorium 0.4.0.2 Beta</span></p><p align="center"><span style=" font-size:10pt;"><br/>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"><span style=" font-size:10pt;">Matt Feemster</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="CodeCopiedGroupBox">
|
||||
<property name="title">
|
||||
<string>Code Copied</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://code.google.com/p/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>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="LibrariesLinkedGroupBox">
|
||||
<property name="title">
|
||||
<string>Libraries Linked</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://qt-project.org"><span style=" text-decoration: underline; color:#0000ff;">Qt</span></a>: Digia Plc (GPL v3, LGPL v2)<br/><a href="http://g-truc.net"><span style=" text-decoration: underline; color:#0000ff;">glm</span></a>: Christophe Riccio (MIT License)<br/><a href="http://threadingbuildingblocks.org"><span style=" text-decoration: underline; color:#0000ff;">Threading Building Blocks</span></a>: Intel Corporation (GPLv2)<br/><a href="http://libjpeg.sourceforge.net"><span style=" text-decoration: underline; color:#0000ff;">libjpeg</span></a>: Independent JPEG Group (Free Software License)<br/><a href="http://libpng.org"><span style=" text-decoration: underline; color:#0000ff;">libpng</span></a>: Glenn Randers-Pehrson et al (Libpng License)<br/><a href="http://xmlsoft.org"><span style=" text-decoration: underline; color:#0000ff;">libxml2</span></a>: Daniel Veillard (MIT License)<br/><a href="http://zlib.net"><span style=" text-decoration: underline; color:#0000ff;">zlib</span></a>: Jean-loup Gailly, Mark Adler (Zlib License)<br/><a href="http://burtleburtle.net/bob/rand/isaac.html"><span style=" text-decoration: underline; color:#0000ff;">QTIsaac</span></a>: Robert J. Jenkins, Quinn Tyler Jackson (Public Domain)<br/><a href="http://cas.ee.ic.ac.uk/people/dt10/index.html"><span style=" text-decoration: underline; color:#0000ff;">MWC64X Random Number Generator</span></a>: David Thomas (Public Domain)<br/><a href="http://code.jellycan.com/simpleopt/"><span style=" text-decoration: underline; color:#0000ff;">SimpleOpt</span></a>: Brodie Thiesfield (MIT License)</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="IconsUsedGroupBox">
|
||||
<property name="title">
|
||||
<string>Icons Used</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<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>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="okButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>okButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>AboutDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>278</x>
|
||||
<y>253</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>96</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
215
Source/Fractorium/DoubleSpinBox.cpp
Normal file
@ -0,0 +1,215 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "DoubleSpinBox.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that passes parent to the base and sets up height and step.
|
||||
/// Specific focus policy is used to allow the user to hover over the control
|
||||
/// and change its value using the mouse wheel without explicitly having to click
|
||||
/// inside of it.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget. Default: NULL.</param>
|
||||
/// <param name="height">The height of the spin box. Default: 16.</param>
|
||||
/// <param name="step">The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05.</param>
|
||||
DoubleSpinBox::DoubleSpinBox(QWidget* parent, int height, double step)
|
||||
: QDoubleSpinBox(parent)
|
||||
{
|
||||
m_Select = false;
|
||||
m_DoubleClick = false;
|
||||
m_DoubleClickNonZero = 0;
|
||||
m_DoubleClickZero = 1;
|
||||
m_Step = step;
|
||||
m_SmallStep = step / 10.0;
|
||||
setSingleStep(step);
|
||||
setFrame(false);
|
||||
setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMinimumHeight(height);//setGeometry() has no effect, so must set both of these instead.
|
||||
setMaximumHeight(height);
|
||||
lineEdit()->installEventFilter(this);
|
||||
lineEdit()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
connect(this, SIGNAL(valueChanged(double)), this, SLOT(onSpinBoxValueChanged(double)), Qt::QueuedConnection);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the control without triggering signals.
|
||||
/// </summary>
|
||||
/// <param name="d">The value to set it to</param>
|
||||
void DoubleSpinBox::SetValueStealth(double d)
|
||||
{
|
||||
blockSignals(true);
|
||||
setValue(d);
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set whether to respond to double click events.
|
||||
/// </summary>
|
||||
/// <param name="b">True if this should respond to double click events, else false.</param>
|
||||
void DoubleSpinBox::DoubleClick(bool b)
|
||||
{
|
||||
m_DoubleClick = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used when the user double clicks the spinner while
|
||||
/// it contains zero.
|
||||
/// </summary>
|
||||
/// <param name="val">The value to be used</param>
|
||||
void DoubleSpinBox::DoubleClickZero(double val)
|
||||
{
|
||||
m_DoubleClickZero = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used when the user double clicks the spinner while
|
||||
/// it contains a non-zero value.
|
||||
/// </summary>
|
||||
/// <param name="val">The value to be used</param>
|
||||
void DoubleSpinBox::DoubleClickNonZero(double val)
|
||||
{
|
||||
m_DoubleClickNonZero = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the default step to be used when the user scrolls.
|
||||
/// </summary>
|
||||
/// <param name="step">The step to use for scrolling</param>
|
||||
void DoubleSpinBox::Step(double step)
|
||||
{
|
||||
m_Step = step;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the small step to be used when the user holds down shift while scrolling.
|
||||
/// The default is step / 10, so use this if something else is needed.
|
||||
/// </summary>
|
||||
/// <param name="step">The small step to use for scrolling while the shift key is down</param>
|
||||
void DoubleSpinBox::SmallStep(double step)
|
||||
{
|
||||
m_SmallStep = step;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expose the underlying QLineEdit control to the caller.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to the QLineEdit</returns>
|
||||
QLineEdit* DoubleSpinBox::lineEdit()
|
||||
{
|
||||
return QDoubleSpinBox::lineEdit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Another workaround for the persistent text selection bug in Qt.
|
||||
/// </summary>
|
||||
void DoubleSpinBox::onSpinBoxValueChanged(double d)
|
||||
{
|
||||
lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event filter for taking special action on double click events.
|
||||
/// </summary>
|
||||
/// <param name="o">The object</param>
|
||||
/// <param name="e">The eevent</param>
|
||||
/// <returns>false</returns>
|
||||
bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
|
||||
{
|
||||
if (e->type() == QMouseEvent::MouseButtonPress && isEnabled())
|
||||
{
|
||||
QPoint pt;
|
||||
|
||||
if (QMouseEvent* me = (QMouseEvent*)e)
|
||||
pt = me->localPos().toPoint();
|
||||
|
||||
int pos = lineEdit()->cursorPositionAt(pt);
|
||||
|
||||
if (lineEdit()->selectedText() != "")
|
||||
{
|
||||
lineEdit()->deselect();
|
||||
lineEdit()->setCursorPosition(pos);
|
||||
return true;
|
||||
}
|
||||
else if (m_Select)
|
||||
{
|
||||
lineEdit()->setCursorPosition(pos);
|
||||
selectAll();
|
||||
m_Select = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m_DoubleClick && e->type() == QMouseEvent::MouseButtonDblClick && isEnabled())
|
||||
{
|
||||
if (IsNearZero(value()))
|
||||
setValue(m_DoubleClickZero);
|
||||
else
|
||||
setValue(m_DoubleClickNonZero);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e->type() == QEvent::Wheel)
|
||||
{
|
||||
//Take special action for shift to reduce the scroll amount. Control already
|
||||
//increases it automatically.
|
||||
if (QWheelEvent* wheelEvent = dynamic_cast<QWheelEvent*>(e))
|
||||
{
|
||||
Qt::KeyboardModifiers mod = wheelEvent->modifiers();
|
||||
|
||||
if (mod.testFlag(Qt::ShiftModifier))
|
||||
setSingleStep(m_SmallStep);
|
||||
else
|
||||
setSingleStep(m_Step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QDoubleSpinBox::eventFilter(o, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus enters the spinner.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void DoubleSpinBox::focusInEvent(QFocusEvent* e)
|
||||
{
|
||||
lineEdit()->setReadOnly(false);
|
||||
QDoubleSpinBox::focusInEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus leaves the spinner.
|
||||
/// Qt has a nasty "feature" that leaves the text in a spinner selected
|
||||
/// and the cursor visible, regardless of whether it has the focus.
|
||||
/// Manually clear both here.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void DoubleSpinBox::focusOutEvent(QFocusEvent* e)
|
||||
{
|
||||
lineEdit()->deselect();//Clear selection when leaving.
|
||||
lineEdit()->setReadOnly(true);//Clever hack to clear the cursor when leaving.
|
||||
QDoubleSpinBox::focusOutEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus enters the spinner.
|
||||
/// Must set the focus to make sure key down messages don't erroneously go to the GLWidget.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void DoubleSpinBox::enterEvent(QEvent* e)
|
||||
{
|
||||
m_Select = true;
|
||||
setFocus();
|
||||
QDoubleSpinBox::enterEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus leaves the spinner.
|
||||
/// Must clear the focus to make sure key down messages don't erroneously go to the GLWidget.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void DoubleSpinBox::leaveEvent(QEvent* e)
|
||||
{
|
||||
m_Select = false;
|
||||
clearFocus();
|
||||
QDoubleSpinBox::leaveEvent(e);
|
||||
}
|
80
Source/Fractorium/DoubleSpinBox.h
Normal file
@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// DoubleSpinBox and VariationTreeDoubleSpinBox classes.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// A derivation to prevent the spin box from selecting its own text
|
||||
/// when editing. Also to prevent multiple spin boxes from all having
|
||||
/// selected text at once.
|
||||
/// </summary>
|
||||
class DoubleSpinBox : public QDoubleSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DoubleSpinBox(QWidget* parent = 0, int height = 16, double step = 0.05);
|
||||
virtual ~DoubleSpinBox() { }
|
||||
void SetValueStealth(double d);
|
||||
void DoubleClick(bool b);
|
||||
void DoubleClickZero(double val);
|
||||
void DoubleClickNonZero(double val);
|
||||
void Step(double step);
|
||||
void SmallStep(double step);
|
||||
QLineEdit* lineEdit();
|
||||
|
||||
public slots:
|
||||
void onSpinBoxValueChanged(double d);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* o, QEvent* e);
|
||||
virtual void focusInEvent(QFocusEvent* e);
|
||||
virtual void focusOutEvent(QFocusEvent* e);
|
||||
virtual void enterEvent(QEvent* e);
|
||||
virtual void leaveEvent(QEvent* e);
|
||||
|
||||
private:
|
||||
bool m_Select;
|
||||
bool m_DoubleClick;
|
||||
double m_DoubleClickNonZero;
|
||||
double m_DoubleClickZero;
|
||||
double m_Step;
|
||||
double m_SmallStep;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for the double spin boxes that are in the
|
||||
/// variations tree.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class VariationTreeDoubleSpinBox : public DoubleSpinBox
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that passes agruments to the base and assigns the m_Param and m_Variation members.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
/// <param name="var">The variation this spinner is for</param>
|
||||
/// <param name="param">The name of the parameter this is for</param>
|
||||
/// <param name="height">The height of the spin box. Default: 16.</param>
|
||||
/// <param name="step">The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05.</param>
|
||||
explicit VariationTreeDoubleSpinBox(QWidget* parent, Variation<T>* var, string param, int height = 16, double step = 0.05)
|
||||
: DoubleSpinBox(parent, height, step)
|
||||
{
|
||||
m_Param = param;
|
||||
m_Variation = var;
|
||||
setDecimals(3);
|
||||
}
|
||||
|
||||
virtual ~VariationTreeDoubleSpinBox() { }
|
||||
bool IsParam() { return !m_Param.empty(); }
|
||||
string ParamName() { return m_Param; }
|
||||
Variation<T>* GetVariation() { return m_Variation; }
|
||||
|
||||
private:
|
||||
string m_Param;
|
||||
Variation<T>* m_Variation;
|
||||
};
|
145
Source/Fractorium/EmberFile.h
Normal file
@ -0,0 +1,145 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// EmberFile class.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Class for representing an ember Xml file in memory.
|
||||
/// It contains a filename and a vector of embers.
|
||||
/// It also provides static helper functions for creating
|
||||
/// default names for the file and the embers in it.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EmberFile
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Empty constructor that does nothing.
|
||||
/// </summary>
|
||||
EmberFile()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="emberFile">The EmberFile object to copy</param>
|
||||
EmberFile(const EmberFile<T>& emberFile)
|
||||
{
|
||||
EmberFile<T>::operator=<T>(emberFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy an EmberFile object of type U.
|
||||
/// </summary>
|
||||
/// <param name="emberFile">The EmberFile object to copy</param>
|
||||
template <typename U>
|
||||
EmberFile(const EmberFile<U>& emberFile)
|
||||
{
|
||||
EmberFile<T>::operator=<U>(emberFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="emberFile">The EmberFile object to copy</param>
|
||||
EmberFile<T>& operator = (const EmberFile<T>& emberFile)
|
||||
{
|
||||
if (this != &emberFile)
|
||||
EmberFile<T>::operator=<T>(emberFile);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator to assign a EmberFile object of type U.
|
||||
/// </summary>
|
||||
/// <param name="emberFile">The EmberFile object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
template <typename U>
|
||||
EmberFile<T>& operator = (const EmberFile<U>& emberFile)
|
||||
{
|
||||
m_Filename = emberFile.m_Filename;
|
||||
CopyVec(m_Embers, emberFile.m_Embers);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the file name and the vector of embers.
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
m_Filename.clear();
|
||||
m_Embers.clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure all ember names are unique.
|
||||
/// </summary>
|
||||
void MakeNamesUnique()
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
for (size_t i = 0; i < m_Embers.size(); i++)
|
||||
{
|
||||
for (size_t j = 0; j < m_Embers.size(); j++)
|
||||
{
|
||||
if (i != j && m_Embers[i].m_Name == m_Embers[j].m_Name)
|
||||
{
|
||||
m_Embers[j].m_Name = m_Embers[j].m_Name + "_" + QString::number(++x).toStdString();
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the default filename based on the current date/time.
|
||||
/// </summary>
|
||||
/// <returns>The default filename</returns>
|
||||
static QString DefaultFilename()
|
||||
{
|
||||
return "Flame_" + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures a given input filename is unique by appending a count to the end.
|
||||
/// </summary>
|
||||
/// <returns>The passed in name if it was unique, else a uniquely made name.</returns>
|
||||
static QString UniqueFilename(QString& filename)
|
||||
{
|
||||
if (!QFile::exists(filename))
|
||||
return filename;
|
||||
|
||||
int counter = 2;
|
||||
QString newPath;
|
||||
QFileInfo original(filename);
|
||||
QString base = original.completeBaseName();
|
||||
QString extension = original.suffix();
|
||||
|
||||
do
|
||||
{
|
||||
newPath = base + "_" + QString::number(counter++) + "." + extension;
|
||||
}
|
||||
while (QFile::exists(newPath));
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the default ember name based on the current date/time and
|
||||
/// the ember's index in the file.
|
||||
/// </summary>
|
||||
/// <param name="i">The index in the file of the ember</param>
|
||||
/// <returns>The default ember name</returns>
|
||||
static QString DefaultEmberName(unsigned int i)
|
||||
{
|
||||
return DefaultFilename() + "-" + QString::number(i);
|
||||
}
|
||||
|
||||
QString m_Filename;
|
||||
vector<Ember<T>> m_Embers;
|
||||
};
|
120
Source/Fractorium/EmberTreeWidgetItem.h
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// EmberTreeWidgetItem
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// A thin derivation of QTreeWidgetItem for a tree of embers in an open file.
|
||||
/// The tree is intended to contain one open ember file at a time.
|
||||
/// This is a non-templated base for casting purposes.
|
||||
/// </summary>
|
||||
class EmberTreeWidgetItemBase : public QTreeWidgetItem
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that takes a pointer to a QTreeWidget as a parent widget.
|
||||
/// This is meant to be a root level item.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget of this item</param>
|
||||
explicit EmberTreeWidgetItemBase(QTreeWidget* parent = 0)
|
||||
: QTreeWidgetItem(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a pointer to a QTreeWidgetItem as a parent widget.
|
||||
/// This is meant to be the child of a root level item.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget of this item</param>
|
||||
explicit EmberTreeWidgetItemBase(QTreeWidgetItem* parent = 0)
|
||||
: QTreeWidgetItem(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the preview image for the tree widget item.
|
||||
/// </summary>
|
||||
/// <param name="v">The vector containing the RGB pixels [0..255] which will make up the preview image</param>
|
||||
/// <param name="width">The width of the image in pixels</param>
|
||||
/// <param name="height">The height of the image in pixels</param>
|
||||
void SetImage(vector<unsigned char>& v, unsigned int width, unsigned int height)
|
||||
{
|
||||
int size = 64;
|
||||
|
||||
m_Image = QImage(width, height, QImage::Format_RGBA8888);
|
||||
memcpy(m_Image.scanLine(0), v.data(), v.size() * sizeof(v[0]));//Memcpy the data in.
|
||||
m_Pixmap = QPixmap::fromImage(m_Image).scaled(QSize(size, size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//Create a QPixmap out of the QImage, scaled to size.
|
||||
setData(0, Qt::DecorationRole, m_Pixmap);
|
||||
}
|
||||
|
||||
protected:
|
||||
QImage m_Image;
|
||||
QPixmap m_Pixmap;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A thin derivation of QTreeWidgetItem for a tree of embers in an open file.
|
||||
/// The tree is intended to contain one open ember file at a time.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EmberTreeWidgetItem : public EmberTreeWidgetItemBase
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that takes a pointer to an ember and a QTreeWidget as a parent widget.
|
||||
/// This is meant to be a root level item.
|
||||
/// </summary>
|
||||
/// <param name="ember">A pointer to the ember this item will represent</param>
|
||||
/// <param name="parent">The parent widget of this item</param>
|
||||
explicit EmberTreeWidgetItem(Ember<T>* ember, QTreeWidget* parent = 0)
|
||||
: EmberTreeWidgetItemBase(parent)
|
||||
{
|
||||
m_Ember = ember;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a pointer to an ember and a QTreeWidgetItem as a parent widget.
|
||||
/// This is meant to be the child of a root level item.
|
||||
/// </summary>
|
||||
/// <param name="ember">A pointer to the ember this item will represent</param>
|
||||
/// <param name="parent">The parent widget of this item</param>
|
||||
explicit EmberTreeWidgetItem(Ember<T>* ember, QTreeWidgetItem* parent = 0)
|
||||
: EmberTreeWidgetItemBase(parent)
|
||||
{
|
||||
m_Ember = ember;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the text of the tree item to the name of the ember.
|
||||
/// </summary>
|
||||
void UpdateEmberName() { m_Ember->m_Name = text(0).toStdString(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the text of the tree item.
|
||||
/// </summary>
|
||||
void UpdateEditText() { setText(0, QString::fromStdString(m_Ember->m_Name)); }
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to the ember held by the tree item.
|
||||
/// </summary>
|
||||
Ember<T>* GetEmber() const { return m_Ember; }
|
||||
|
||||
/// <summary>
|
||||
/// Perform a deep copy from the passed in ember to the dereferenced
|
||||
/// ember pointer of the tree item.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to copy</param>
|
||||
void SetEmber(Ember<T>& ember) { *m_Ember = ember; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the ember pointer member to point to the passed in ember pointer.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to point to</param>
|
||||
void SetEmberPointer(Ember<T>* ember) { m_Ember = ember; }
|
||||
|
||||
private:
|
||||
Ember<T>* m_Ember;
|
||||
};
|
550
Source/Fractorium/FinalRenderDialog.cpp
Normal file
@ -0,0 +1,550 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "FinalRenderDialog.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which sets up the GUI for the final rendering dialog.
|
||||
/// Settings used to populate widgets with initial values.
|
||||
/// This function contains the render function as a lambda.
|
||||
/// </summary>
|
||||
/// <param name="settings">Pointer to the global settings object to use</param>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
/// <param name="f">The window flags. Default: 0.</param>
|
||||
FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* settings, QWidget* parent, Qt::WindowFlags f)
|
||||
: QDialog(parent, f)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
int row = 0, spinHeight = 20;
|
||||
unsigned int i;
|
||||
QTableWidget* table = ui.FinalRenderGeometryTable;
|
||||
QTableWidgetItem* item = NULL;
|
||||
|
||||
m_Fractorium = (Fractorium*)parent;
|
||||
m_Settings = settings;
|
||||
ui.FinalRenderThreadCountSpin->setRange(1, Timing::ProcessorCount());
|
||||
connect(ui.FinalRenderEarlyClipCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnEarlyClipCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderTransparencyCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnTransparencyCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderOpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderDoublePrecisionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoublePrecisionCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderPlatformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnPlatformComboCurrentIndexChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderDoAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoAllCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderKeepAspectCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnKeepAspectCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderScaleNoneRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderScaleWidthRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
|
||||
connect(ui.FinalRenderScaleHeightRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
|
||||
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_WidthSpin, spinHeight, 10, 100000, 50, SIGNAL(valueChanged(int)), SLOT(OnWidthChanged(int)), true, 1980);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_HeightSpin, spinHeight, 10, 100000, 50, SIGNAL(valueChanged(int)), SLOT(OnHeightChanged(int)), true, 1080);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_QualitySpin, spinHeight, 1, 200000, 50, SIGNAL(valueChanged(int)), SLOT(OnQualityChanged(int)), true, 1000);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_TemporalSamplesSpin, spinHeight, 1, 5000, 50, SIGNAL(valueChanged(int)), SLOT(OnTemporalSamplesChanged(int)), true, 1000);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 2);
|
||||
|
||||
row++;//Memory usage.
|
||||
|
||||
TwoButtonWidget* tbw = new TwoButtonWidget("...", "Open", 22, 40, 24, table);
|
||||
table->setCellWidget(row, 1, tbw);
|
||||
table->item(row++, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
connect(tbw->m_Button1, SIGNAL(clicked(bool)), this, SLOT(OnFileButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(tbw->m_Button2, SIGNAL(clicked(bool)), this, SLOT(OnShowFolderButtonClicked(bool)), Qt::QueuedConnection);
|
||||
|
||||
m_PrefixEdit = new QLineEdit(table);
|
||||
table->setCellWidget(row++, 1, m_PrefixEdit);
|
||||
|
||||
m_SuffixEdit = new QLineEdit(table);
|
||||
table->setCellWidget(row++, 1, m_SuffixEdit);
|
||||
|
||||
ui.StartRenderButton->disconnect(SIGNAL(clicked(bool)));
|
||||
connect(ui.StartRenderButton, SIGNAL(clicked(bool)), this, SLOT(OnRenderClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.StopRenderButton, SIGNAL(clicked(bool)), this, SLOT(OnCancelRenderClicked(bool)), Qt::QueuedConnection);
|
||||
|
||||
if (m_Wrapper.CheckOpenCL())
|
||||
{
|
||||
vector<string> platforms = m_Wrapper.PlatformNames();
|
||||
|
||||
//Populate combo boxes with available OpenCL platforms and devices.
|
||||
for (i = 0; i < platforms.size(); i++)
|
||||
ui.FinalRenderPlatformCombo->addItem(QString::fromStdString(platforms[i]));
|
||||
|
||||
//If init succeeds, set the selected platform and device combos to match what was saved in the settings.
|
||||
if (m_Wrapper.Init(m_Settings->FinalPlatformIndex(), m_Settings->FinalDeviceIndex()))
|
||||
{
|
||||
ui.FinalRenderOpenCLCheckBox->setChecked( m_Settings->FinalOpenCL());
|
||||
ui.FinalRenderPlatformCombo->setCurrentIndex(m_Settings->FinalPlatformIndex());
|
||||
ui.FinalRenderDeviceCombo->setCurrentIndex( m_Settings->FinalDeviceIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
OnPlatformComboCurrentIndexChanged(0);
|
||||
ui.FinalRenderOpenCLCheckBox->setChecked(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui.FinalRenderOpenCLCheckBox->setChecked(false);
|
||||
ui.FinalRenderOpenCLCheckBox->setEnabled(false);
|
||||
}
|
||||
|
||||
ui.FinalRenderEarlyClipCheckBox->setChecked( m_Settings->FinalEarlyClip());
|
||||
ui.FinalRenderTransparencyCheckBox->setChecked( m_Settings->FinalTransparency());
|
||||
ui.FinalRenderDoublePrecisionCheckBox->setChecked(m_Settings->FinalDouble());
|
||||
ui.FinalRenderSaveXmlCheckBox->setChecked( m_Settings->FinalSaveXml());
|
||||
ui.FinalRenderDoAllCheckBox->setChecked( m_Settings->FinalDoAll());
|
||||
ui.FinalRenderDoSequenceCheckBox->setChecked( m_Settings->FinalDoSequence());
|
||||
ui.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect());
|
||||
ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount());
|
||||
|
||||
m_WidthSpin->setValue(m_Settings->FinalWidth());
|
||||
m_HeightSpin->setValue(m_Settings->FinalHeight());
|
||||
m_QualitySpin->setValue(m_Settings->FinalQuality());
|
||||
m_TemporalSamplesSpin->setValue(m_Settings->FinalTemporalSamples());
|
||||
m_SupersampleSpin->setValue(m_Settings->FinalSupersample());
|
||||
|
||||
Scale((eScaleType)m_Settings->FinalScale());
|
||||
|
||||
if (m_Settings->FinalDoAllExt() == "jpg")
|
||||
ui.FinalRenderJpgRadioButton->setChecked(true);
|
||||
else
|
||||
ui.FinalRenderPngRadioButton->setChecked(true);
|
||||
|
||||
//Explicitly call these to enable/disable the appropriate controls.
|
||||
OnOpenCLCheckBoxStateChanged(ui.FinalRenderOpenCLCheckBox->isChecked());
|
||||
OnDoAllCheckBoxStateChanged(ui.FinalRenderDoAllCheckBox->isChecked());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GUI settings wrapper functions, getters only.
|
||||
/// </summary>
|
||||
|
||||
bool FractoriumFinalRenderDialog::EarlyClip() { return ui.FinalRenderEarlyClipCheckBox->isChecked(); }
|
||||
bool FractoriumFinalRenderDialog::Transparency() { return ui.FinalRenderTransparencyCheckBox->isChecked(); }
|
||||
bool FractoriumFinalRenderDialog::OpenCL() { return ui.FinalRenderOpenCLCheckBox->isChecked(); }
|
||||
bool FractoriumFinalRenderDialog::Double() { return ui.FinalRenderDoublePrecisionCheckBox->isChecked(); }
|
||||
bool FractoriumFinalRenderDialog::SaveXml() { return ui.FinalRenderSaveXmlCheckBox->isChecked(); }
|
||||
bool FractoriumFinalRenderDialog::DoAll() { return ui.FinalRenderDoAllCheckBox->isChecked(); }
|
||||
bool FractoriumFinalRenderDialog::DoSequence() { return ui.FinalRenderDoSequenceCheckBox->isChecked(); }
|
||||
bool FractoriumFinalRenderDialog::KeepAspect() { return ui.FinalRenderKeepAspectCheckBox->isChecked(); }
|
||||
QString FractoriumFinalRenderDialog::DoAllExt() { return ui.FinalRenderJpgRadioButton->isChecked() ? "jpg" : "png"; }
|
||||
QString FractoriumFinalRenderDialog::Path() { return ui.FinalRenderGeometryTable->item(6, 1)->text(); }
|
||||
void FractoriumFinalRenderDialog::Path(QString s) { ui.FinalRenderGeometryTable->item(6, 1)->setText(s); }
|
||||
QString FractoriumFinalRenderDialog::Prefix() { return m_PrefixEdit->text(); }
|
||||
QString FractoriumFinalRenderDialog::Suffix() { return m_SuffixEdit->text(); }
|
||||
unsigned int FractoriumFinalRenderDialog::PlatformIndex() { return ui.FinalRenderPlatformCombo->currentIndex(); }
|
||||
unsigned int FractoriumFinalRenderDialog::DeviceIndex() { return ui.FinalRenderDeviceCombo->currentIndex(); }
|
||||
unsigned int FractoriumFinalRenderDialog::ThreadCount() { return ui.FinalRenderThreadCountSpin->value(); }
|
||||
unsigned int FractoriumFinalRenderDialog::Width() { return m_WidthSpin->value(); }
|
||||
unsigned int FractoriumFinalRenderDialog::Height() { return m_HeightSpin->value(); }
|
||||
unsigned int FractoriumFinalRenderDialog::Quality() { return m_QualitySpin->value(); }
|
||||
unsigned int FractoriumFinalRenderDialog::TemporalSamples() { return m_TemporalSamplesSpin->value(); }
|
||||
unsigned int FractoriumFinalRenderDialog::Supersample() { return m_SupersampleSpin->value(); }
|
||||
|
||||
/// <summary>
|
||||
/// Capture the current state of the Gui.
|
||||
/// Used to hold options for performing the final render.
|
||||
/// </summary>
|
||||
/// <returns>The state of the Gui as a struct</returns>
|
||||
FinalRenderGuiState FractoriumFinalRenderDialog::State()
|
||||
{
|
||||
FinalRenderGuiState state;
|
||||
|
||||
state.m_EarlyClip = EarlyClip();
|
||||
state.m_Transparency = Transparency();
|
||||
state.m_OpenCL = OpenCL();
|
||||
state.m_Double = Double();
|
||||
state.m_SaveXml = SaveXml();
|
||||
state.m_DoAll = DoAll();
|
||||
state.m_DoSequence = DoSequence();
|
||||
state.m_KeepAspect = KeepAspect();
|
||||
state.m_Scale = Scale();
|
||||
state.m_Path = Path();
|
||||
state.m_DoAllExt = DoAllExt();
|
||||
state.m_Prefix = Prefix();
|
||||
state.m_Suffix = Suffix();
|
||||
state.m_PlatformIndex = PlatformIndex();
|
||||
state.m_DeviceIndex = DeviceIndex();
|
||||
state.m_ThreadCount = ThreadCount();
|
||||
state.m_Width = Width();
|
||||
state.m_Height = Height();
|
||||
state.m_Quality = Quality();
|
||||
state.m_TemporalSamples = TemporalSamples();
|
||||
state.m_Supersample = Supersample();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the type of scaling desired based on what radio button has been selected.
|
||||
/// </summary>
|
||||
/// <returns>The type of scaling as an eScaleType enum</returns>
|
||||
eScaleType FractoriumFinalRenderDialog::Scale()
|
||||
{
|
||||
if (ui.FinalRenderScaleNoneRadioButton->isChecked())
|
||||
return SCALE_NONE;
|
||||
else if (ui.FinalRenderScaleWidthRadioButton->isChecked())
|
||||
return SCALE_WIDTH;
|
||||
else if (ui.FinalRenderScaleHeightRadioButton->isChecked())
|
||||
return SCALE_HEIGHT;
|
||||
else
|
||||
return SCALE_NONE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the type of scaling desired which will select the corresponding radio button.
|
||||
/// </summary>
|
||||
/// <param name="scale">The type of scaling to use</param>
|
||||
void FractoriumFinalRenderDialog::Scale(eScaleType scale)
|
||||
{
|
||||
if (scale == SCALE_NONE)
|
||||
ui.FinalRenderScaleNoneRadioButton->setChecked(true);
|
||||
else if (scale == SCALE_WIDTH)
|
||||
ui.FinalRenderScaleWidthRadioButton->setChecked(true);
|
||||
else if (scale == SCALE_HEIGHT)
|
||||
ui.FinalRenderScaleHeightRadioButton->setChecked(true);
|
||||
else
|
||||
ui.FinalRenderScaleNoneRadioButton->setChecked(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple wrapper to put moving the cursor to the end in a signal
|
||||
/// so it can be called from a thread.
|
||||
/// </summary>
|
||||
void FractoriumFinalRenderDialog::MoveCursorToEnd()
|
||||
{
|
||||
ui.FinalRenderTextOutput->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use early clipping before spatial filtering.
|
||||
/// </summary>
|
||||
/// <param name="index">True to early clip, else don't.</param>
|
||||
void FractoriumFinalRenderDialog::OnEarlyClipCheckBoxStateChanged(int state)
|
||||
{
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use transparency in png images.
|
||||
/// </summary>
|
||||
/// <param name="index">True to use transparency, else don't.</param>
|
||||
void FractoriumFinalRenderDialog::OnTransparencyCheckBoxStateChanged(int state)
|
||||
{
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set whether to use OpenCL in the rendering process or not.
|
||||
/// </summary>
|
||||
/// <param name="state">Use OpenCL if state == Qt::Checked, else don't.</param>
|
||||
void FractoriumFinalRenderDialog::OnOpenCLCheckBoxStateChanged(int state)
|
||||
{
|
||||
bool checked = state == Qt::Checked;
|
||||
|
||||
ui.FinalRenderPlatformCombo->setEnabled(checked);
|
||||
ui.FinalRenderDeviceCombo->setEnabled(checked);
|
||||
ui.FinalRenderThreadCountSpin->setEnabled(!checked);
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set whether to use double or single precision in the rendering process or not.
|
||||
/// This will recreate the entire controller.
|
||||
/// </summary>
|
||||
/// <param name="state">Use double if state == Qt::Checked, else float.</param>
|
||||
void FractoriumFinalRenderDialog::OnDoublePrecisionCheckBoxStateChanged(int state)
|
||||
{
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the the device combo box with all available
|
||||
/// OpenCL devices for the selected platform.
|
||||
/// Called when the platform combo box index changes.
|
||||
/// </summary>
|
||||
/// <param name="index">The selected index of the combo box</param>
|
||||
void FractoriumFinalRenderDialog::OnPlatformComboCurrentIndexChanged(int index)
|
||||
{
|
||||
vector<string> devices = m_Wrapper.DeviceNames(index);
|
||||
|
||||
ui.FinalRenderDeviceCombo->clear();
|
||||
|
||||
for (size_t i = 0; i < devices.size(); i++)
|
||||
ui.FinalRenderDeviceCombo->addItem(QString::fromStdString(devices[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The do all checkbox was changed.
|
||||
/// If checked, render all embers available in the currently opened file, else
|
||||
/// only render the current ember.
|
||||
/// </summary>
|
||||
/// <param name="state">The state of the checkbox</param>
|
||||
void FractoriumFinalRenderDialog::OnDoAllCheckBoxStateChanged(int state)
|
||||
{
|
||||
ui.FinalRenderDoSequenceCheckBox->setEnabled(ui.FinalRenderDoAllCheckBox->isChecked());
|
||||
ui.FinalRenderExtensionGroupBox->setEnabled(ui.FinalRenderDoAllCheckBox->isChecked());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to keep the aspect ratio of the desired width and height the same
|
||||
/// as that of the original width and height.
|
||||
/// </summary>
|
||||
/// <param name="checked">The state of the checkbox</param>
|
||||
void FractoriumFinalRenderDialog::OnKeepAspectCheckBoxStateChanged(int state)
|
||||
{
|
||||
if (state && m_Controller.get())
|
||||
m_HeightSpin->SetValueStealth(m_WidthSpin->value() / m_Controller->OriginalAspect());
|
||||
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The scaling method radio button selection was changed.
|
||||
/// </summary>
|
||||
/// <param name="checked">The state of the radio button</param>
|
||||
void FractoriumFinalRenderDialog::OnScaleRadioButtonChanged(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The width spinner was changed, recompute required memory.
|
||||
/// If the aspect ratio checkbox is checked, set the value of
|
||||
/// the height spinner as well to be in proportion.
|
||||
/// </summary>
|
||||
/// <param name="d">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnWidthChanged(int d)
|
||||
{
|
||||
if (ui.FinalRenderKeepAspectCheckBox->isChecked() && m_Controller.get())
|
||||
m_HeightSpin->SetValueStealth(m_WidthSpin->value() / m_Controller->OriginalAspect());
|
||||
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The height spinner was changed, recompute required memory.
|
||||
/// If the aspect ratio checkbox is checked, set the value of
|
||||
/// the width spinner as well to be in proportion.
|
||||
/// </summary>
|
||||
/// <param name="d">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnHeightChanged(int d)
|
||||
{
|
||||
if (ui.FinalRenderKeepAspectCheckBox->isChecked() && m_Controller.get())
|
||||
m_WidthSpin->SetValueStealth(m_HeightSpin->value() * m_Controller->OriginalAspect());
|
||||
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The quality spinner was changed, recompute required memory.
|
||||
/// </summary>
|
||||
/// <param name="d">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnQualityChanged(int d)
|
||||
{
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The temporal samples spinner was changed, recompute required memory.
|
||||
/// </summary>
|
||||
/// <param name="d">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnTemporalSamplesChanged(int d)
|
||||
{
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The supersample spinner was changed, recompute required memory.
|
||||
/// </summary>
|
||||
/// <param name="d">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnSupersampleChanged(int d)
|
||||
{
|
||||
SetMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a single ember is being rendered, show the save file dialog.
|
||||
/// If a more than one is being rendered, show the save folder dialog.
|
||||
/// Called when the ... file button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnFileButtonClicked(bool checked)
|
||||
{
|
||||
bool doAll = ui.FinalRenderDoAllCheckBox->isChecked();
|
||||
QString filename;
|
||||
|
||||
if (doAll)
|
||||
filename = m_Fractorium->SetupSaveFolderDialog();
|
||||
else
|
||||
filename = m_Fractorium->SetupSaveImageDialog(m_Controller->Name());
|
||||
|
||||
if (filename != "")
|
||||
{
|
||||
if (doAll)
|
||||
{
|
||||
if (!filename.endsWith(QDir::separator()))
|
||||
filename += "/";
|
||||
}
|
||||
|
||||
QFileInfo fileInfo(filename);
|
||||
QString path = fileInfo.absolutePath();
|
||||
|
||||
m_Settings->SaveFolder(path);//Any time they exit the box with a valid value, preserve it in the settings.
|
||||
Path(filename);
|
||||
SetMemory();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the folder where the last rendered image was saved to.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnShowFolderButtonClicked(bool checked)
|
||||
{
|
||||
QString text = Path();
|
||||
|
||||
if (text != "")
|
||||
{
|
||||
QFileInfo fileInfo(text);
|
||||
QString path = fileInfo.absolutePath();
|
||||
QDir dir(path);
|
||||
|
||||
if (dir.exists())
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the render process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnRenderClicked(bool checked)
|
||||
{
|
||||
if (CreateControllerFromGUI(true))
|
||||
m_Controller->Render();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the render.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void FractoriumFinalRenderDialog::OnCancelRenderClicked(bool checked)
|
||||
{
|
||||
if (m_Controller.get())
|
||||
m_Controller->CancelRender();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the options and the ember to populate widgets.
|
||||
/// Called when the dialog is initially shown.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
|
||||
{
|
||||
#ifdef DO_DOUBLE
|
||||
Ember<double> ed;
|
||||
#else
|
||||
Ember<float> ed;
|
||||
#endif
|
||||
|
||||
if (CreateControllerFromGUI(true))
|
||||
{
|
||||
m_Fractorium->m_Controller->CopyEmber(ed);//Copy the current ember from the main window out in to a temp.
|
||||
m_Controller->SetEmber(ed);//Copy the temp into the final render controller.
|
||||
m_Controller->SetOriginalEmber(ed);
|
||||
SetMemory();
|
||||
m_Controller->ResetProgress();
|
||||
}
|
||||
|
||||
QDir dir(m_Settings->SaveFolder());
|
||||
QString name = m_Controller->Name();
|
||||
|
||||
if (dir.exists() && name != "")
|
||||
Path(dir.absolutePath() + "/" + name + ".png");
|
||||
|
||||
ui.FinalRenderTextOutput->clear();
|
||||
QDialog::showEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the dialog without running, or if running, cancel and exit.
|
||||
/// Settings will not be saved.
|
||||
/// Control will be returned to Fractorium::OnActionFinalRender().
|
||||
/// </summary>
|
||||
void FractoriumFinalRenderDialog::reject()
|
||||
{
|
||||
if (m_Controller.get())
|
||||
{
|
||||
m_Controller->CancelRender();
|
||||
m_Controller->DeleteRenderer();
|
||||
}
|
||||
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the controller from the options and optionally its underlying renderer.
|
||||
/// </summary>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer)
|
||||
{
|
||||
bool ok = true;
|
||||
#ifdef DO_DOUBLE
|
||||
size_t size = Double() ? sizeof(double) : sizeof(float);
|
||||
Ember<double> ed;
|
||||
Ember<double> orig;
|
||||
EmberFile<double> efd;
|
||||
#else
|
||||
size_t size = sizeof(float);
|
||||
Ember<float> ed;
|
||||
Ember<float> orig;
|
||||
EmberFile<float> efd;
|
||||
#endif
|
||||
|
||||
if (!m_Controller.get() || (m_Controller->SizeOfT() != size))
|
||||
{
|
||||
//First check if a controller has already been created, and if so, save its embers and gracefully shut it down.
|
||||
if (m_Controller.get())
|
||||
{
|
||||
m_Controller->CopyEmber(ed);//Convert float to double or save double verbatim;
|
||||
m_Controller->CopyEmberFile(efd);
|
||||
m_Controller->Shutdown();
|
||||
}
|
||||
|
||||
//Create a float or double controller based on the GUI.
|
||||
#ifdef DO_DOUBLE
|
||||
if (Double())
|
||||
m_Controller = auto_ptr<FinalRenderEmberControllerBase>(new FinalRenderEmberController<double>(this));
|
||||
else
|
||||
#endif
|
||||
m_Controller = auto_ptr<FinalRenderEmberControllerBase>(new FinalRenderEmberController<float>(this));
|
||||
|
||||
//Restore the ember and ember file.
|
||||
if (m_Controller.get())
|
||||
{
|
||||
m_Controller->SetEmber(ed);//Convert float to double or set double verbatim;
|
||||
m_Controller->SetEmberFile(efd);
|
||||
m_Fractorium->m_Controller->CopyEmber(orig);//Copy the current ember from the main window out in to a temp.
|
||||
m_Controller->SetOriginalEmber(orig);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Controller.get())
|
||||
{
|
||||
if (createRenderer)
|
||||
return m_Controller->CreateRendererFromGUI();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the amount of memory needed via call to SyncAndComputeMemory(), then
|
||||
/// assign the result to the table cell as text.
|
||||
/// </summary>
|
||||
void FractoriumFinalRenderDialog::SetMemory()
|
||||
{
|
||||
if (isVisible() && CreateControllerFromGUI(true))
|
||||
ui.FinalRenderGeometryTable->item(5, 1)->setText(QLocale(QLocale::English).toString(m_Controller->SyncAndComputeMemory()));
|
||||
}
|
113
Source/Fractorium/FinalRenderDialog.h
Normal file
@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_FinalRenderDialog.h"
|
||||
#include "SpinBox.h"
|
||||
#include "DoubleSpinBox.h"
|
||||
#include "TwoButtonWidget.h"
|
||||
#include "FractoriumSettings.h"
|
||||
#include "FinalRenderEmberController.h"
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumFinalRenderDialog class.
|
||||
/// </summary>
|
||||
|
||||
class Fractorium;//Forward declaration since Fractorium uses this dialog.
|
||||
|
||||
/// <summary>
|
||||
/// The final render dialog is for when the user is satisfied with the parameters they've
|
||||
/// set and they want to do a final render at a higher quality and at a specific resolution
|
||||
/// and save it out to a file.
|
||||
/// It supports rendering either the current ember, or all of them in the opened file.
|
||||
/// If a single ember is rendered, it will be saved to a single output file.
|
||||
/// If multiple embers are rendered, they will all be saved to a specified directory using
|
||||
/// default names.
|
||||
/// The user can optionally save the Xml file with each ember.
|
||||
/// They can be treated as individual images, or as an animation sequence in which case
|
||||
/// motion blurring with temporal samples will be applied.
|
||||
/// It keeps a pointer to the main window and the global settings object for convenience.
|
||||
/// The settings used here are saved to the /finalrender portion of the settings file.
|
||||
/// It has its own OpenCLWrapper member for populating the combo boxes.
|
||||
/// Upon running, it will delete the main window's renderer to save memory/GPU resources and restore it to its
|
||||
/// original state upon exiting.
|
||||
/// This class uses a controller-based design similar to the main window.
|
||||
/// </summary>
|
||||
class FractoriumFinalRenderDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend Fractorium;
|
||||
friend FinalRenderEmberControllerBase;
|
||||
friend FinalRenderEmberController<float>;
|
||||
#ifdef DO_DOUBLE
|
||||
friend FinalRenderEmberController<double>;
|
||||
#endif
|
||||
|
||||
public:
|
||||
FractoriumFinalRenderDialog(FractoriumSettings* settings, QWidget* parent, Qt::WindowFlags f = 0);
|
||||
bool EarlyClip();
|
||||
bool Transparency();
|
||||
bool OpenCL();
|
||||
bool Double();
|
||||
bool SaveXml();
|
||||
bool DoAll();
|
||||
bool DoSequence();
|
||||
bool KeepAspect();
|
||||
eScaleType Scale();
|
||||
void Scale(eScaleType scale);
|
||||
QString DoAllExt();
|
||||
QString Path();
|
||||
void Path(QString s);
|
||||
QString Prefix();
|
||||
QString Suffix();
|
||||
unsigned int PlatformIndex();
|
||||
unsigned int DeviceIndex();
|
||||
unsigned int ThreadCount();
|
||||
unsigned int Width();
|
||||
unsigned int Height();
|
||||
unsigned int Quality();
|
||||
unsigned int TemporalSamples();
|
||||
unsigned int Supersample();
|
||||
FinalRenderGuiState State();
|
||||
|
||||
public Q_SLOTS:
|
||||
void MoveCursorToEnd();
|
||||
void OnEarlyClipCheckBoxStateChanged(int state);
|
||||
void OnTransparencyCheckBoxStateChanged(int state);
|
||||
void OnOpenCLCheckBoxStateChanged(int state);
|
||||
void OnDoublePrecisionCheckBoxStateChanged(int state);
|
||||
void OnPlatformComboCurrentIndexChanged(int index);
|
||||
void OnDoAllCheckBoxStateChanged(int state);
|
||||
void OnKeepAspectCheckBoxStateChanged(int state);
|
||||
void OnScaleRadioButtonChanged(bool checked);
|
||||
void OnWidthChanged(int d);
|
||||
void OnHeightChanged(int d);
|
||||
void OnQualityChanged(int d);
|
||||
void OnTemporalSamplesChanged(int d);
|
||||
void OnSupersampleChanged(int d);
|
||||
void OnFileButtonClicked(bool checked);
|
||||
void OnShowFolderButtonClicked(bool checked);
|
||||
void OnRenderClicked(bool checked);
|
||||
void OnCancelRenderClicked(bool checked);
|
||||
|
||||
protected:
|
||||
virtual void reject();
|
||||
virtual void showEvent(QShowEvent* e);
|
||||
|
||||
private:
|
||||
bool CreateControllerFromGUI(bool createRenderer);
|
||||
void SetMemory();
|
||||
|
||||
OpenCLWrapper m_Wrapper;
|
||||
Timing m_RenderTimer;
|
||||
SpinBox* m_WidthSpin;
|
||||
SpinBox* m_HeightSpin;
|
||||
SpinBox* m_QualitySpin;
|
||||
SpinBox* m_TemporalSamplesSpin;
|
||||
SpinBox* m_SupersampleSpin;
|
||||
QLineEdit* m_PrefixEdit;
|
||||
QLineEdit* m_SuffixEdit;
|
||||
FractoriumSettings* m_Settings;
|
||||
Fractorium* m_Fractorium;
|
||||
auto_ptr<FinalRenderEmberControllerBase> m_Controller;
|
||||
Ui::FinalRenderDialog ui;
|
||||
};
|
904
Source/Fractorium/FinalRenderDialog.ui
Normal file
@ -0,0 +1,904 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FinalRenderDialog</class>
|
||||
<widget class="QDialog" name="FinalRenderDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>519</width>
|
||||
<height>801</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Final Render</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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="gridLayout" columnstretch="0,0">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="FinalRenderDoAllCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Render all open flames instead of just the current one</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Render All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="FinalRenderDoSequenceCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Use temporal samples value to achieve motion blur effect between flames</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Render as Animation Sequence</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="FinalRenderDoublePrecisionCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Checked: use 64-bit double precision numbers (slower, but better image quality).</p><p>Unchecked: use 32-bit single precision numbers (faster, but worse image quality).</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Double Precision</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="FinalRenderOpenCLCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use OpenCL to render if your video card supports it.</p><p>This is highly recommended as it will dramatically speed up render time.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use OpenCL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="FinalRenderEarlyClipCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Checked: clip colors and gamma correct after density filtering.</p><p>Unchecked: do it after spatial filtering.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Early Clip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="FinalRenderSaveXmlCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Save an Xml parameter file for each flame rendered</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save Xml</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="FinalRenderTransparencyCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use transparency in the final image.</p><p>Only supported for Png format.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Transparency</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QGroupBox" name="FinalRenderScaleGroupBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The scaling to perform from the editor to the final rendered image</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Scale</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="FinalRenderScaleNoneRadioButton">
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="FinalRenderScaleWidthRadioButton">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="FinalRenderScaleHeightRadioButton">
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QGroupBox" name="FinalRenderExtensionGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The image type to save the final output as when rendering all open flames</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Render All Extension</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="FinalRenderJpgRadioButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Jpg</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="FinalRenderPngRadioButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Png</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="FinalRenderKeepAspectCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Maintain the aspect ratio between width and height to be equal to base width and base height</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keep Aspect Ratio</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="FinalRenderPreviewLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sizeIncrement">
|
||||
<size>
|
||||
<width>1</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="FinalRenderPlatformCombo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="FinalRenderDeviceCombo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="FinalRenderThreadCountSpin">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The number of threads to use with CPU rendering.</p><p>Decrease for a more responsive system during rendering, increase for better performance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>Threads </string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="TableWidget" name="FinalRenderGeometryTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>218</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>218</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</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="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::SolidLine</enum>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>110</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>35</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Quality</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Temporal Samples</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Supersample</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Memory Usage</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Output</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Prefix</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Suffix</string>
|
||||
</property>
|
||||
</row>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Field</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
<item row="0" column="0">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The width in pixels of the final output image</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The height in pixels of the final output image</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<property name="text">
|
||||
<string>Quality</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The quality in iterations per pixel of the final output image</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<property name="text">
|
||||
<string>Temporal Samples</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The number of interpolated renders to do for each flame when rendering as an animation sequence</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<property name="text">
|
||||
<string>Supersample</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The number to multiply the dimensions of the histogram and density filtering buffer by to achieve anti-aliasing.</p><p>Use this very sparingly as it increases the required memory by n squared.</p></body></html></string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<property name="text">
|
||||
<string>Memory Usage</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory including the final output image required to perform this render</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<property name="text">
|
||||
<string>Output</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The output file path for rendering a single flame, or folder location for rendering multiple flames</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<property name="text">
|
||||
<string>Prefix</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The prefix to attach to all image filenames</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<property name="text">
|
||||
<string>Suffix</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The suffix to attach to all image filenames</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="FinalRenderImageCountLabel">
|
||||
<property name="text">
|
||||
<string>0 / 0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,0,0" columnstretch="1,4">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item row="2" column="1">
|
||||
<widget class="QProgressBar" name="FinalRenderFilteringProgress">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="FinalRenderIterationProgressLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Iteration:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="FinalRenderAccumProgressLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Final Accumulation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QProgressBar" name="FinalRenderIterationProgress">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QProgressBar" name="FinalRenderAccumProgress">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="FinalRenderFilteringProgressLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Density Filtering:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="FinalRenderTotalProgressLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Total Progress:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QProgressBar" name="FinalRenderTotalProgress">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="FinalRenderTextOutput">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="FinalRenderButtonHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="FinalRenderButtonSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>131</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="StartRenderButton">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="StopRenderButton">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="CloseButton">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TableWidget</class>
|
||||
<extends>QTableWidget</extends>
|
||||
<header>TableWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>StartRenderButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>FinalRenderDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>278</x>
|
||||
<y>253</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>96</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>CloseButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>FinalRenderDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>369</x>
|
||||
<y>253</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>179</x>
|
||||
<y>282</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
553
Source/Fractorium/FinalRenderEmberController.cpp
Normal file
@ -0,0 +1,553 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "FractoriumEmberController.h"
|
||||
#include "FinalRenderEmberController.h"
|
||||
#include "FinalRenderDialog.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which accepts a pointer to the final render dialog.
|
||||
/// It passes a pointer to the main window to the base and initializes members.
|
||||
/// </summary>
|
||||
/// <param name="finalRender">Pointer to the final render dialog</param>
|
||||
FinalRenderEmberControllerBase::FinalRenderEmberControllerBase(FractoriumFinalRenderDialog* finalRender)
|
||||
: FractoriumEmberControllerBase(finalRender->m_Fractorium)
|
||||
{
|
||||
m_Run = false;
|
||||
m_PreviewRun = false;
|
||||
m_ImageCount = 0;
|
||||
m_FinishedImageCount = 0;
|
||||
m_FinalRender = finalRender;
|
||||
m_Settings = m_Fractorium->m_Settings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the render by calling Abort().
|
||||
/// This will block until the cancelling is actually finished.
|
||||
/// It should never take longer than a few milliseconds because the
|
||||
/// renderer checks the m_Abort flag in many places during the process.
|
||||
/// </summary>
|
||||
void FinalRenderEmberControllerBase::CancelRender()
|
||||
{
|
||||
if (m_Result.isRunning())
|
||||
{
|
||||
tbb::task_group g;
|
||||
|
||||
g.run([&]
|
||||
{
|
||||
m_Run = false;
|
||||
|
||||
if (m_Renderer.get())
|
||||
{
|
||||
m_Renderer->Abort();
|
||||
|
||||
while (m_Renderer->InRender())
|
||||
QApplication::processEvents();
|
||||
|
||||
m_Renderer->EnterRender();
|
||||
m_Renderer->EnterFinalAccum();
|
||||
m_Renderer->LeaveFinalAccum();
|
||||
m_Renderer->LeaveRender();
|
||||
}
|
||||
});
|
||||
|
||||
g.wait();
|
||||
|
||||
while (m_Result.isRunning())
|
||||
QApplication::processEvents();
|
||||
|
||||
m_FinalRender->ui.FinalRenderTextOutput->append("Render canceled.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new renderer based on the options selected on the GUI.
|
||||
/// If a renderer matching the options has already been created, no action is taken.
|
||||
/// </summary>
|
||||
/// <returns>True if a valid renderer is created or if no action is taken, else false.</returns>
|
||||
bool FinalRenderEmberControllerBase::CreateRendererFromGUI()
|
||||
{
|
||||
bool useOpenCL = m_Wrapper.CheckOpenCL() && m_FinalRender->OpenCL();
|
||||
|
||||
return CreateRenderer(useOpenCL ? OPENCL_RENDERER : CPU_RENDERER,
|
||||
m_FinalRender->PlatformIndex(),
|
||||
m_FinalRender->DeviceIndex(),
|
||||
false);//Not shared.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which accepts a pointer to the final render dialog and passes it to the base.
|
||||
/// The main final rendering lambda function is constructed here.
|
||||
/// </summary>
|
||||
/// <param name="finalRender">Pointer to the final render dialog</param>
|
||||
template<typename T>
|
||||
FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender)
|
||||
: FinalRenderEmberControllerBase(finalRender)
|
||||
{
|
||||
m_PreviewRenderer = auto_ptr<EmberNs::Renderer<T, T>>(new EmberNs::Renderer<T, T>());
|
||||
m_PreviewRenderer->Callback(NULL);
|
||||
m_PreviewRenderer->NumChannels(4);
|
||||
m_PreviewRenderer->ReclaimOnResize(true);
|
||||
|
||||
m_PreviewRenderFunc = [&]()
|
||||
{
|
||||
m_PreviewCs.Enter();//Thread prep.
|
||||
m_PreviewRun = true;
|
||||
m_PreviewRenderer->Abort();
|
||||
|
||||
QLabel* widget = m_FinalRender->ui.FinalRenderPreviewLabel;
|
||||
unsigned int maxDim = 100u;
|
||||
T scalePercentage;
|
||||
|
||||
//Determine how to scale the scaled ember to fit in the label with a max of 100x100.
|
||||
if (m_Ember.m_FinalRasW >= m_Ember.m_FinalRasH)
|
||||
scalePercentage = T(maxDim) / m_Ember.m_FinalRasW;
|
||||
else
|
||||
scalePercentage = T(maxDim) / m_Ember.m_FinalRasH;
|
||||
|
||||
m_PreviewEmber = m_Ember;
|
||||
m_PreviewEmber.m_Quality = 100;
|
||||
m_PreviewEmber.m_Supersample = 1;
|
||||
m_PreviewEmber.m_TemporalSamples = 1;
|
||||
m_PreviewEmber.m_FinalRasW = min(maxDim, unsigned int(scalePercentage * m_Ember.m_FinalRasW));
|
||||
m_PreviewEmber.m_FinalRasH = min(maxDim, unsigned int(scalePercentage * m_Ember.m_FinalRasH));
|
||||
m_PreviewEmber.m_PixelsPerUnit = scalePercentage * m_Ember.m_PixelsPerUnit;
|
||||
|
||||
while (!m_PreviewRenderer->Aborted() || m_PreviewRenderer->InRender())
|
||||
QApplication::processEvents();
|
||||
|
||||
m_PreviewRenderer->EarlyClip(m_FinalRender->EarlyClip());
|
||||
m_PreviewRenderer->Transparency(m_FinalRender->Transparency());
|
||||
m_PreviewRenderer->SetEmber(m_PreviewEmber);
|
||||
|
||||
if (m_PreviewRenderer->Run(m_PreviewFinalImage) == RENDER_OK)
|
||||
{
|
||||
QImage image(m_PreviewEmber.m_FinalRasW, m_PreviewEmber.m_FinalRasH, QImage::Format_RGBA8888);//The label wants RGBA.
|
||||
memcpy(image.scanLine(0), m_PreviewFinalImage.data(), m_PreviewFinalImage.size() * sizeof(m_PreviewFinalImage[0]));//Memcpy the data in.
|
||||
QPixmap pixmap = QPixmap::fromImage(image);
|
||||
QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap));
|
||||
}
|
||||
|
||||
m_PreviewRun = false;
|
||||
m_PreviewCs.Leave();
|
||||
};
|
||||
|
||||
//The main rendering function which will be called in a Qt thread.
|
||||
//A backup Xml is made before the rendering process starts just in case it crashes before finishing.
|
||||
//If it finishes successfully, delete the backup file.
|
||||
m_RenderFunc = [&]()
|
||||
{
|
||||
size_t i;
|
||||
|
||||
m_Run = true;
|
||||
m_TotalTimer.Tic();//Begin timing for progress.
|
||||
m_GuiState = m_FinalRender->State();//Cache render settings from the GUI before running.
|
||||
m_FinishedImageCount = 0;
|
||||
|
||||
QFileInfo original(m_GuiState.m_Path);
|
||||
QString backup = original.absolutePath() + QDir::separator() + m_GuiState.m_Prefix + original.completeBaseName() + m_GuiState.m_Suffix + "_backup.flame";
|
||||
|
||||
QMetaObject::invokeMethod(m_Fractorium, "OnActionSaveCurrentToOpenedFile", Qt::QueuedConnection, Q_ARG(bool, true));//First, save the current ember back to its opened file.
|
||||
m_Fractorium->m_Controller->CopyEmber(m_Ember);
|
||||
m_Fractorium->m_Controller->CopyEmberFile(m_EmberFile);//Copy the whole file, will take about 0.2ms per ember in the file.
|
||||
|
||||
//Save backup Xml.
|
||||
if (m_GuiState.m_DoAll && m_EmberFile.m_Embers.size() > 1)
|
||||
m_XmlWriter.Save(backup.toStdString().c_str(), m_EmberFile.m_Embers, 0, true, false, true);
|
||||
else
|
||||
m_XmlWriter.Save(backup.toStdString().c_str(), m_Ember, 0, true, false, true);
|
||||
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTextOutput, "setText", Qt::QueuedConnection, Q_ARG(QString, "Begin rendering..."));
|
||||
m_Renderer->EarlyClip(m_GuiState.m_EarlyClip);
|
||||
m_Renderer->ThreadCount(m_GuiState.m_ThreadCount);
|
||||
m_Renderer->Transparency(m_GuiState.m_Transparency);
|
||||
|
||||
if (m_GuiState.m_Path.endsWith(".png", Qt::CaseInsensitive) || m_Renderer->RendererType() == OPENCL_RENDERER)
|
||||
m_Renderer->NumChannels(4);
|
||||
else
|
||||
m_Renderer->NumChannels(3);
|
||||
|
||||
//The rendering process is different between doing a single image, and doing multiple.
|
||||
if (m_GuiState.m_DoAll && m_EmberFile.m_Embers.size() > 1)
|
||||
{
|
||||
m_ImageCount = m_EmberFile.m_Embers.size();
|
||||
ResetProgress();
|
||||
|
||||
//Different action required for rendering as animation or not.
|
||||
if (m_GuiState.m_DoSequence)
|
||||
{
|
||||
//Need to loop through and set all w, h, q, ts, ss and t vals.
|
||||
for (i = 0; i < m_EmberFile.m_Embers.size() && m_Run; i++)
|
||||
{
|
||||
Sync(m_EmberFile.m_Embers[i]);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
if (m_EmberFile.m_Embers[i].m_Time <= m_EmberFile.m_Embers[i - 1].m_Time)
|
||||
m_EmberFile.m_Embers[i].m_Time = m_EmberFile.m_Embers[i - 1].m_Time + 1;
|
||||
}
|
||||
else if (i == 0)
|
||||
{
|
||||
m_EmberFile.m_Embers[i].m_Time = 0;
|
||||
}
|
||||
|
||||
m_EmberFile.m_Embers[i].m_TemporalSamples = m_GuiState.m_TemporalSamples;
|
||||
}
|
||||
|
||||
m_Renderer->SetEmber(m_EmberFile.m_Embers);//Copy all embers to the local storage inside the renderer.
|
||||
|
||||
//Render each image, cancelling if m_Run ever gets set to false.
|
||||
for (i = 0; i < m_EmberFile.m_Embers.size() && m_Run; i++)
|
||||
{
|
||||
m_Renderer->Reset();//Have to manually set this since the ember is not set each time through.
|
||||
m_RenderTimer.Tic();//Toc() is called in the progress function.
|
||||
|
||||
if (m_Renderer->Run(m_FinalImage, i) != RENDER_OK)
|
||||
{
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTextOutput, "append", Qt::QueuedConnection, Q_ARG(QString, "Renderering failed.\n"));
|
||||
m_Fractorium->ErrorReportToQTextEdit(m_Renderer->ErrorReport(), m_FinalRender->ui.FinalRenderTextOutput, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else//Render all images, but not as an animation sequence (without temporal samples motion blur).
|
||||
{
|
||||
//Copy widget values to all embers
|
||||
for (i = 0; i < m_EmberFile.m_Embers.size() && m_Run; i++)
|
||||
{
|
||||
Sync(m_EmberFile.m_Embers[i]);
|
||||
m_EmberFile.m_Embers[i].m_TemporalSamples = 1;//No temporal sampling.
|
||||
}
|
||||
|
||||
//Render each image, cancelling if m_Run ever gets set to false.
|
||||
for (i = 0; i < m_EmberFile.m_Embers.size() && m_Run; i++)
|
||||
{
|
||||
m_Renderer->SetEmber(m_EmberFile.m_Embers[i]);
|
||||
m_RenderTimer.Tic();//Toc() is called in the progress function.
|
||||
|
||||
if (m_Renderer->Run(m_FinalImage) != RENDER_OK)
|
||||
{
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTextOutput, "append", Qt::QueuedConnection, Q_ARG(QString, "Renderering failed.\n"));
|
||||
m_Fractorium->ErrorReportToQTextEdit(m_Renderer->ErrorReport(), m_FinalRender->ui.FinalRenderTextOutput, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else//Render a single image.
|
||||
{
|
||||
m_ImageCount = 1;
|
||||
Sync(m_Ember);
|
||||
ResetProgress();
|
||||
m_Ember.m_TemporalSamples = 1;
|
||||
m_Renderer->SetEmber(m_Ember);
|
||||
m_RenderTimer.Tic();//Toc() is called in the progress function.
|
||||
|
||||
if (m_Renderer->Run(m_FinalImage) != RENDER_OK)
|
||||
{
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTextOutput, "append", Qt::QueuedConnection, Q_ARG(QString, "Renderering failed.\n"));
|
||||
m_Fractorium->ErrorReportToQTextEdit(m_Renderer->ErrorReport(), m_FinalRender->ui.FinalRenderTextOutput, false);
|
||||
}
|
||||
}
|
||||
|
||||
QFile::remove(backup);
|
||||
m_Run = false;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setters for embers and ember files which convert between float and double types.
|
||||
/// These are used to preserve the current ember/file when switching between renderers.
|
||||
/// Note that some precision will be lost when going from double to float.
|
||||
/// </summary>
|
||||
template <typename T> void FinalRenderEmberController<T>::SetEmber(const Ember<float>& ember, bool verbatim) { m_Ember = ember; }
|
||||
template <typename T> void FinalRenderEmberController<T>::CopyEmber(Ember<float>& ember) { ember = m_Ember; }
|
||||
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile) { m_EmberFile = emberFile; }
|
||||
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile) { emberFile = m_EmberFile; }
|
||||
template <typename T> double FinalRenderEmberController<T>::OriginalAspect() { return double(m_OriginalEmber.m_OrigFinalRasW) / m_OriginalEmber.m_OrigFinalRasH; }
|
||||
#ifdef DO_DOUBLE
|
||||
template <typename T> void FinalRenderEmberController<T>::SetEmber(const Ember<double>& ember, bool verbatim) { m_Ember = ember; }
|
||||
template <typename T> void FinalRenderEmberController<T>::CopyEmber(Ember<double>& ember) { ember = m_Ember; }
|
||||
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile) { m_EmberFile = emberFile; }
|
||||
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile) { emberFile = m_EmberFile; }
|
||||
template <typename T> void FinalRenderEmberController<T>::SetOriginalEmber(Ember<double>& ember) { m_OriginalEmber = ember; }
|
||||
#else
|
||||
template <typename T> void FinalRenderEmberController<T>::SetOriginalEmber(Ember<float>& ember) { m_OriginalEmber = ember; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Progress function.
|
||||
/// Take special action to sync options upon finishing.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember currently being rendered</param>
|
||||
/// <param name="foo">An extra dummy parameter</param>
|
||||
/// <param name="fraction">The progress fraction from 0-100</param>
|
||||
/// <param name="stage">The stage of iteration. 1 is iterating, 2 is density filtering, 2 is final accumulation.</param>
|
||||
/// <param name="etaMs">The estimated milliseconds to completion of the current stage</param>
|
||||
/// <returns>0 if the user has clicked cancel, else 1 to continue rendering.</returns>
|
||||
template <typename T>
|
||||
int FinalRenderEmberController<T>::ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs)
|
||||
{
|
||||
static int count = 0;
|
||||
|
||||
if (stage == 0)
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderIterationProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int(fraction)));
|
||||
else if (stage == 1)
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderFilteringProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int(fraction)));
|
||||
else if (stage == 2)
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderAccumProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int(fraction)));
|
||||
|
||||
//Finished, so take special action.
|
||||
if (stage == 2 && (int)fraction == 100)
|
||||
{
|
||||
string renderTimeString = m_RenderTimer.Format(m_RenderTimer.Toc()), totalTimeString;
|
||||
QString status, filename = m_GuiState.m_Path;
|
||||
QFileInfo original(filename);
|
||||
EmberStats stats = m_Renderer->Stats();
|
||||
QString iters = QLocale(QLocale::English).toString(stats.m_Iters);
|
||||
|
||||
if (m_GuiState.m_DoAll && m_EmberFile.m_Embers.size() > 1)
|
||||
filename = original.absolutePath() + QDir::separator() + m_GuiState.m_Prefix + QString::fromStdString(m_EmberFile.m_Embers[m_FinishedImageCount].m_Name) + m_GuiState.m_Suffix + "." + m_GuiState.m_DoAllExt;
|
||||
else
|
||||
filename = original.absolutePath() + QDir::separator() + m_GuiState.m_Prefix + original.completeBaseName() + m_GuiState.m_Suffix + "." + original.suffix();
|
||||
|
||||
filename = EmberFile<T>::UniqueFilename(filename);
|
||||
|
||||
//Save whatever options were specified on the GUI to the settings.
|
||||
m_Settings->FinalEarlyClip(m_GuiState.m_EarlyClip);
|
||||
m_Settings->FinalTransparency(m_GuiState.m_Transparency);
|
||||
m_Settings->FinalOpenCL(m_GuiState.m_OpenCL);
|
||||
m_Settings->FinalDouble(m_GuiState.m_Double);
|
||||
m_Settings->FinalPlatformIndex(m_GuiState.m_PlatformIndex);
|
||||
m_Settings->FinalDeviceIndex(m_GuiState.m_DeviceIndex);
|
||||
m_Settings->FinalSaveXml(m_GuiState.m_SaveXml);
|
||||
m_Settings->FinalDoAll(m_GuiState.m_DoAll);
|
||||
m_Settings->FinalDoSequence(m_GuiState.m_DoSequence);
|
||||
m_Settings->FinalKeepAspect(m_GuiState.m_KeepAspect);
|
||||
m_Settings->FinalScale(m_GuiState.m_Scale);
|
||||
m_Settings->FinalDoAllExt(m_GuiState.m_DoAllExt);
|
||||
m_Settings->FinalThreadCount(m_GuiState.m_ThreadCount);
|
||||
m_Settings->FinalWidth(m_GuiState.m_Width);
|
||||
m_Settings->FinalHeight(m_GuiState.m_Height);
|
||||
m_Settings->FinalQuality(m_GuiState.m_Quality);
|
||||
m_Settings->FinalTemporalSamples(m_GuiState.m_TemporalSamples);
|
||||
m_Settings->FinalSupersample(m_GuiState.m_Supersample);
|
||||
SaveCurrentRender(filename);
|
||||
|
||||
if (m_GuiState.m_SaveXml)
|
||||
{
|
||||
QFileInfo xmlFileInfo(filename);//Create another one in case it was modified for batch rendering.
|
||||
QString newPath = xmlFileInfo.absolutePath() + QDir::separator() + xmlFileInfo.completeBaseName() + ".flame";
|
||||
xmlDocPtr tempEdit = ember.m_Edits;
|
||||
|
||||
ember.m_Edits = m_XmlWriter.CreateNewEditdoc(&ember, NULL, "edit", m_Settings->Nick().toStdString(), m_Settings->Url().toStdString(), m_Settings->Id().toStdString(), "", 0, 0);
|
||||
m_XmlWriter.Save(newPath.toStdString().c_str(), ember, 0, true, false, true);//Note that the ember passed is used, rather than m_Ember because it's what was actually rendered.
|
||||
|
||||
if (tempEdit != NULL)
|
||||
xmlFreeDoc(tempEdit);
|
||||
}
|
||||
|
||||
m_FinishedImageCount++;
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int(((float)m_FinishedImageCount / (float)m_ImageCount) * 100)));
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(QString, QString::number(m_FinishedImageCount) + " / " + QString::number(m_ImageCount)));
|
||||
|
||||
status = "Image " + QString::number(m_FinishedImageCount) + ":\nPure render time: " + QString::fromStdString(renderTimeString);
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTextOutput, "append", Qt::QueuedConnection, Q_ARG(QString, status));
|
||||
|
||||
totalTimeString = m_TotalTimer.Format(m_TotalTimer.Toc());
|
||||
status = "Total render time: " + QString::fromStdString(totalTimeString) + "\nTotal iters: " + iters + "\n";
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTextOutput, "append", Qt::QueuedConnection, Q_ARG(QString, status));
|
||||
QMetaObject::invokeMethod(m_FinalRender, "MoveCursorToEnd", Qt::QueuedConnection);
|
||||
|
||||
if (m_FinishedImageCount != m_ImageCount)
|
||||
{
|
||||
ResetProgress(false);
|
||||
}
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTextOutput, "update", Qt::QueuedConnection);
|
||||
QApplication::processEvents();
|
||||
return m_Run ? 1 : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the final rendering process.
|
||||
/// Create the needed renderer from the GUI if it has not been created yet.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
template<typename T>
|
||||
bool FinalRenderEmberController<T>::Render()
|
||||
{
|
||||
QString filename = m_FinalRender->Path();
|
||||
|
||||
if (filename == "")
|
||||
{
|
||||
QMessageBox::critical(m_FinalRender, "File Error", "Please enter a valid path and filename for the output.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CreateRendererFromGUI())
|
||||
{
|
||||
m_FinalRender->ui.FinalRenderTextOutput->clear();
|
||||
|
||||
//Note that a Qt thread must be used, rather than a tbb task.
|
||||
//This is because tbb does a very poor job of allocating thread resources
|
||||
//and dedicates an entire core just to this thread which does nothing waiting for the
|
||||
//parallel iteration loops inside of the CPU renderer to finish. The result is that
|
||||
//the renderer ends up using ThreadCount - 1 to iterate, instead of ThreadCount.
|
||||
//By using a Qt thread here, and tbb inside the renderer, all cores can be maxed out.
|
||||
m_Result = QtConcurrent::run(m_RenderFunc);
|
||||
m_Settings->sync();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop rendering and initialize a new renderer, using the specified type and the options on the final render dialog.
|
||||
/// </summary>
|
||||
/// <param name="renderType">The type of render to create</param>
|
||||
/// <param name="platform">The index platform of the platform to use</param>
|
||||
/// <param name="device">The index device of the device to use</param>
|
||||
/// <param name="outputTexID">The texture ID of the shared OpenGL texture if shared</param>
|
||||
/// <param name="shared">True if shared with OpenGL, else false. Default: true.</param>
|
||||
/// <returns>True if nothing went wrong, else false.</returns>
|
||||
template <typename T>
|
||||
bool FinalRenderEmberController<T>::CreateRenderer(eRendererType renderType, unsigned int platform, unsigned int device, bool shared)
|
||||
{
|
||||
bool ok = true;
|
||||
vector<string> errorReport;
|
||||
QString filename = m_FinalRender->Path();
|
||||
unsigned int channels = filename.endsWith(".png", Qt::CaseInsensitive) ? 4 : 3;
|
||||
|
||||
CancelRender();
|
||||
|
||||
if (m_Renderer.get() &&
|
||||
m_Renderer->Ok() &&
|
||||
m_Renderer->RendererType() == renderType &&
|
||||
m_Platform == platform &&
|
||||
m_Device == device &&
|
||||
m_Shared == shared)
|
||||
{
|
||||
return ok;
|
||||
}
|
||||
|
||||
if (!m_Renderer.get() || (m_Renderer->RendererType() != renderType))
|
||||
{
|
||||
EmberReport emberReport;
|
||||
vector<string> errorReport;
|
||||
|
||||
m_Platform = platform;//Store values for re-creation later on.
|
||||
m_Device = device;
|
||||
m_OutputTexID = 0;//Don't care about tex ID when doing final render.
|
||||
m_Shared = shared;
|
||||
|
||||
m_Renderer = auto_ptr<EmberNs::RendererBase>(::CreateRenderer<T, T>(renderType, platform, device, shared, m_OutputTexID, emberReport));
|
||||
errorReport = emberReport.ErrorReport();
|
||||
|
||||
if (!errorReport.empty())
|
||||
{
|
||||
ok = false;
|
||||
QMessageBox::critical(m_Fractorium, "Renderer Creation Error", "Could not create requested renderer, fallback CPU renderer created. See info tab for details.");
|
||||
m_Fractorium->ErrorReportToQTextEdit(errorReport, m_Fractorium->ui.InfoRenderingTextEdit);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Renderer.get())
|
||||
{
|
||||
if (m_Renderer->RendererType() == OPENCL_RENDERER)
|
||||
channels = 4;//Always using 4 since the GL texture is RGBA.
|
||||
|
||||
m_Renderer->Callback(this);
|
||||
m_Renderer->NumChannels(channels);
|
||||
m_Renderer->ReclaimOnResize(false);
|
||||
m_Renderer->EarlyClip(m_FinalRender->EarlyClip());
|
||||
m_Renderer->ThreadCount(m_FinalRender->ThreadCount());
|
||||
m_Renderer->Transparency(m_FinalRender->Transparency());
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
QMessageBox::critical(m_FinalRender, "Renderer Creation Error", "Could not create renderer, aborting. See info tab for details.");
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set various parameters in the renderer and current ember with the values
|
||||
/// specified in the widgets and compute the amount of memory required to render.
|
||||
/// This includes the memory needed for the final output image.
|
||||
/// </summary>
|
||||
/// <returns>If successful, memory required in bytes, else zero.</returns>
|
||||
template <typename T>
|
||||
unsigned __int64 FinalRenderEmberController<T>::SyncAndComputeMemory()
|
||||
{
|
||||
if (m_Renderer.get())
|
||||
{
|
||||
bool b = false;
|
||||
QString filename = m_FinalRender->Path();
|
||||
unsigned int channels = filename.endsWith(".png", Qt::CaseInsensitive) ? 4 : 3;//4 channels for Png, else 3.
|
||||
|
||||
Sync(m_Ember);
|
||||
m_Renderer->SetEmber(m_Ember);
|
||||
m_Renderer->CreateSpatialFilter(b);
|
||||
m_Renderer->CreateTemporalFilter(b);
|
||||
m_Renderer->NumChannels(channels);
|
||||
m_Renderer->ComputeBounds();
|
||||
CancelPreviewRender();
|
||||
//m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc);
|
||||
//while (!m_PreviewResult.isRunning()) { QApplication::processEvents(); }//Wait for it to start up.
|
||||
m_PreviewRenderFunc();
|
||||
return m_Renderer->MemoryRequired(true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the progress bars.
|
||||
/// </summary>
|
||||
/// <param name="total">True to reset render image and total progress bars, else false to only do iter, filter and accum bars.</param>
|
||||
template <typename T>
|
||||
void FinalRenderEmberController<T>::ResetProgress(bool total)
|
||||
{
|
||||
if (total)
|
||||
{
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(QString, "0 / " + QString::number(m_ImageCount)));
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderIterationProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderFilteringProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||
QMetaObject::invokeMethod(m_FinalRender->ui.FinalRenderAccumProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void FinalRenderEmberController<T>::CancelPreviewRender()
|
||||
{
|
||||
m_PreviewRenderer->Abort();
|
||||
|
||||
while (m_PreviewRenderer->InRender()) { QApplication::processEvents(); }
|
||||
while (m_PreviewRun) { QApplication::processEvents(); }
|
||||
while (m_PreviewResult.isRunning()) { QApplication::processEvents(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy widget values to the ember passed in.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember whose values will be modified</param>
|
||||
template <typename T>
|
||||
void FinalRenderEmberController<T>::Sync(Ember<T>& ember)
|
||||
{
|
||||
int w = m_FinalRender->m_WidthSpin->value();
|
||||
int h = m_FinalRender->m_HeightSpin->value();
|
||||
|
||||
ember.m_FinalRasW = m_OriginalEmber.m_OrigFinalRasW;//Scale is always in terms of the original dimensions of the ember in the editor.
|
||||
ember.m_FinalRasH = m_OriginalEmber.m_OrigFinalRasH;
|
||||
ember.m_PixelsPerUnit = m_OriginalEmber.m_PixelsPerUnit;
|
||||
ember.SetSizeAndAdjustScale(w, h, false, m_FinalRender->Scale());
|
||||
ember.m_Quality = m_FinalRender->m_QualitySpin->value();
|
||||
ember.m_Supersample = m_FinalRender->m_SupersampleSpin->value();
|
||||
|
||||
if (m_FinalRender->ui.FinalRenderDoSequenceCheckBox->isChecked())
|
||||
ember.m_TemporalSamples = m_FinalRender->m_TemporalSamplesSpin->value();
|
||||
}
|
143
Source/Fractorium/FinalRenderEmberController.h
Normal file
@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumSettings.h"
|
||||
#include "FractoriumEmberController.h"
|
||||
|
||||
/// <summary>
|
||||
/// FinalRenderEmberControllerBase and FinalRenderEmberController<T> classes.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumEmberController and Fractorium need each other, but each can't include the other.
|
||||
/// So Fractorium includes this file, and Fractorium is declared as a forward declaration here.
|
||||
/// </summary>
|
||||
class Fractorium;
|
||||
class FractoriumFinalRenderDialog;
|
||||
|
||||
/// <summary>
|
||||
/// Used to hold the options specified in the current state of the Gui for performing the final render.
|
||||
/// </summary>
|
||||
struct FinalRenderGuiState
|
||||
{
|
||||
bool m_EarlyClip;
|
||||
bool m_AlphaChannel;
|
||||
bool m_Transparency;
|
||||
bool m_OpenCL;
|
||||
bool m_Double;
|
||||
bool m_SaveXml;
|
||||
bool m_DoAll;
|
||||
bool m_DoSequence;
|
||||
bool m_KeepAspect;
|
||||
eScaleType m_Scale;
|
||||
QString m_Path;
|
||||
QString m_DoAllExt;
|
||||
QString m_Prefix;
|
||||
QString m_Suffix;
|
||||
unsigned int m_PlatformIndex;
|
||||
unsigned int m_DeviceIndex;
|
||||
unsigned int m_ThreadCount;
|
||||
unsigned int m_Width;
|
||||
unsigned int m_Height;
|
||||
unsigned int m_Quality;
|
||||
unsigned int m_TemporalSamples;
|
||||
unsigned int m_Supersample;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// FinalRenderEmberControllerBase serves as a non-templated base class with virtual
|
||||
/// functions which will be overridden in a derived class that takes a template parameter.
|
||||
/// Although not meant to be used as an interactive renderer, it derives from FractoriumEmberControllerBase
|
||||
/// to access a few of its members to avoid having to redefine them here.
|
||||
/// </summary>
|
||||
class FinalRenderEmberControllerBase : public FractoriumEmberControllerBase
|
||||
{
|
||||
friend FractoriumFinalRenderDialog;
|
||||
|
||||
public:
|
||||
FinalRenderEmberControllerBase(FractoriumFinalRenderDialog* finalRender);
|
||||
virtual ~FinalRenderEmberControllerBase() { }
|
||||
|
||||
virtual unsigned __int64 SyncAndComputeMemory() { return 0; }
|
||||
virtual QString Name() const { return ""; }
|
||||
virtual void ResetProgress(bool total = true) { }
|
||||
#ifdef DO_DOUBLE
|
||||
virtual void SetOriginalEmber(Ember<double>& ember) { }
|
||||
#else
|
||||
virtual void SetOriginalEmber(Ember<float>& ember) { }
|
||||
#endif
|
||||
virtual double OriginalAspect() { return 1; }
|
||||
|
||||
void CancelRender();
|
||||
bool CreateRendererFromGUI();
|
||||
|
||||
protected:
|
||||
bool m_Run;
|
||||
bool m_PreviewRun;
|
||||
unsigned int m_ImageCount;
|
||||
unsigned int m_FinishedImageCount;
|
||||
|
||||
QFuture<void> m_Result;
|
||||
QFuture<void> m_PreviewResult;
|
||||
std::function<void (void)> m_RenderFunc;
|
||||
std::function<void (void)> m_PreviewRenderFunc;
|
||||
vector<unsigned char> m_PreviewFinalImage;
|
||||
|
||||
FractoriumSettings* m_Settings;
|
||||
FractoriumFinalRenderDialog* m_FinalRender;
|
||||
FinalRenderGuiState m_GuiState;
|
||||
OpenCLWrapper m_Wrapper;
|
||||
CriticalSection m_PreviewCs;
|
||||
Timing m_RenderTimer;
|
||||
Timing m_TotalTimer;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Templated derived class which implements all interaction functionality between the embers
|
||||
/// of a specific template type and the final render dialog;
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
class FinalRenderEmberController : public FinalRenderEmberControllerBase
|
||||
{
|
||||
public:
|
||||
FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender);
|
||||
virtual ~FinalRenderEmberController() { }
|
||||
|
||||
virtual void SetEmber(const Ember<float>& ember, bool verbatim = false);
|
||||
virtual void CopyEmber(Ember<float>& ember);
|
||||
virtual void SetEmberFile(const EmberFile<float>& emberFile);
|
||||
virtual void CopyEmberFile(EmberFile<float>& emberFile);
|
||||
#ifdef DO_DOUBLE
|
||||
virtual void SetEmber(const Ember<double>& ember, bool verbatim = false);
|
||||
virtual void CopyEmber(Ember<double>& ember);
|
||||
virtual void SetEmberFile(const EmberFile<double>& emberFile);
|
||||
virtual void CopyEmberFile(EmberFile<double>& emberFile);
|
||||
virtual void SetOriginalEmber(Ember<double>& ember);
|
||||
#else
|
||||
virtual void SetOriginalEmber(Ember<float>& ember);
|
||||
#endif
|
||||
virtual double OriginalAspect();
|
||||
virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs);
|
||||
virtual bool Render();
|
||||
virtual bool CreateRenderer(eRendererType renderType, unsigned int platform, unsigned int device, bool shared = true);
|
||||
virtual unsigned int SizeOfT() { return sizeof(T); }
|
||||
virtual unsigned __int64 SyncAndComputeMemory();
|
||||
virtual QString Name() const { return QString::fromStdString(m_Ember.m_Name); }
|
||||
virtual void ResetProgress(bool total = true);
|
||||
void CancelPreviewRender();
|
||||
|
||||
protected:
|
||||
void Sync(Ember<T>& ember);
|
||||
|
||||
Ember<T> m_Ember;
|
||||
Ember<T> m_OriginalEmber;
|
||||
Ember<T> m_PreviewEmber;
|
||||
EmberFile<T> m_EmberFile;
|
||||
EmberToXml<T> m_XmlWriter;
|
||||
auto_ptr<EmberNs::Renderer<T, T>> m_PreviewRenderer;
|
||||
};
|
||||
|
||||
template class FinalRenderEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FinalRenderEmberController<double>;
|
||||
#endif
|
BIN
Source/Fractorium/Fractorium.aps
Normal file
544
Source/Fractorium/Fractorium.cpp
Normal file
@ -0,0 +1,544 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that initializes the entire program.
|
||||
/// The setup process is very lengthy because it requires many custom modifications
|
||||
/// to the GUI widgets that are not possible to do through the designer. So if something
|
||||
/// is present here, it's safe to assume it can't be done in the designer.
|
||||
/// </summary>
|
||||
Fractorium::Fractorium(QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
{
|
||||
int spinHeight = 20;
|
||||
size_t i, j;
|
||||
Timing t;
|
||||
ui.setupUi(this);
|
||||
qRegisterMetaType<QVector<int>>("QVector<int>");//For previews.
|
||||
qRegisterMetaType<vector<unsigned char>>("vector<unsigned char>");
|
||||
qRegisterMetaType<EmberTreeWidgetItemBase*>("EmberTreeWidgetItemBase*");
|
||||
|
||||
m_FontSize = 9;
|
||||
m_VarSortMode = 1;//Sort by weight by default.
|
||||
m_ColorDialog = new QColorDialog(this);
|
||||
m_Settings = new FractoriumSettings(this);
|
||||
|
||||
m_FileDialog = NULL;//Use lazy instantiation upon first use.
|
||||
m_FolderDialog = NULL;
|
||||
m_FinalRenderDialog = new FractoriumFinalRenderDialog(m_Settings, this);
|
||||
m_OptionsDialog = new FractoriumOptionsDialog(m_Settings, this);
|
||||
m_AboutDialog = new FractoriumAboutDialog(this);
|
||||
|
||||
//The options dialog should be a fixed size without a size grip, however even if it's here, it still shows up. Perhaps Qt will fix it some day.
|
||||
m_OptionsDialog->layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
m_OptionsDialog->setSizeGripEnabled(false);
|
||||
connect(m_ColorDialog, SIGNAL(colorSelected(const QColor&)), this, SLOT(OnColorSelected(const QColor&)), Qt::QueuedConnection);
|
||||
|
||||
InitToolbarUI();
|
||||
InitParamsUI();
|
||||
InitXformsUI();
|
||||
InitXformsColorUI();
|
||||
InitXformsAffineUI();
|
||||
InitXformsVariationsUI();
|
||||
InitXformsXaosUI();
|
||||
InitPaletteUI();
|
||||
InitLibraryUI();
|
||||
InitMenusUI();
|
||||
|
||||
//This will init the controller and fill in the variations and palette tables with template specific instances
|
||||
//of their respective objects.
|
||||
#ifdef DO_DOUBLE
|
||||
if (m_Settings->Double())
|
||||
m_Controller = auto_ptr<FractoriumEmberControllerBase>(new FractoriumEmberController<double>(this));
|
||||
else
|
||||
#endif
|
||||
m_Controller = auto_ptr<FractoriumEmberControllerBase>(new FractoriumEmberController<float>(this));
|
||||
|
||||
if (m_Wrapper.CheckOpenCL() && m_Settings->OpenCL() && m_QualitySpin->value() < 30)
|
||||
m_QualitySpin->setValue(30);
|
||||
|
||||
int statusBarHeight = 20;
|
||||
ui.statusBar->setMinimumHeight(statusBarHeight);
|
||||
ui.statusBar->setMaximumHeight(statusBarHeight);
|
||||
|
||||
m_RenderStatusLabel = new QLabel(this);
|
||||
m_RenderStatusLabel->setMinimumWidth(200);
|
||||
m_RenderStatusLabel->setAlignment(Qt::AlignRight);
|
||||
ui.statusBar->addPermanentWidget(m_RenderStatusLabel);
|
||||
|
||||
m_CoordinateStatusLabel = new QLabel(this);
|
||||
m_CoordinateStatusLabel->setMinimumWidth(300);
|
||||
m_CoordinateStatusLabel->setMaximumWidth(300);
|
||||
m_CoordinateStatusLabel->setAlignment(Qt::AlignLeft);
|
||||
ui.statusBar->addWidget(m_CoordinateStatusLabel);
|
||||
|
||||
int progressBarHeight = 15;
|
||||
int progressBarWidth = 300;
|
||||
m_ProgressBar = new QProgressBar(this);
|
||||
m_ProgressBar->setRange(0, 100);
|
||||
m_ProgressBar->setValue(0);
|
||||
m_ProgressBar->setMinimumHeight(progressBarHeight);
|
||||
m_ProgressBar->setMaximumHeight(progressBarHeight);
|
||||
m_ProgressBar->setMinimumWidth(progressBarWidth);
|
||||
m_ProgressBar->setMaximumWidth(progressBarWidth);
|
||||
ui.statusBar->addPermanentWidget(m_ProgressBar);
|
||||
|
||||
showMaximized();
|
||||
|
||||
connect(ui.DockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(dockLocationChanged(Qt::DockWidgetArea)));
|
||||
connect(ui.DockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(OnDockTopLevelChanged(bool)));
|
||||
|
||||
//Always ensure the library tab is selected, which will show preview renders.
|
||||
ui.ParamsTabWidget->setCurrentIndex(0);
|
||||
ui.XformsTabWidget->setCurrentIndex(2);//Make variations tab the currently selected one under the Xforms tab.
|
||||
|
||||
//Setting certain values will completely throw off the GUI, doing everything
|
||||
//from setting strange margins, to arbitrarily changing the fonts used.
|
||||
//For these cases, the only way to fix the problem is to use style sheets.
|
||||
ui.ColorTable->setStyleSheet("QTableWidget::item { padding: 1px; }");
|
||||
ui.GeometryTable->setStyleSheet("QTableWidget::item { padding: 1px; }");
|
||||
ui.FilterTable->setStyleSheet("QTableWidget::item { padding: 1px; }");
|
||||
ui.IterationTable->setStyleSheet("QTableWidget::item { padding: 1px; }");
|
||||
ui.AffineTab->setStyleSheet("QTableWidget::item { padding: 1px; }");
|
||||
ui.XformWeightNameTable->setStyleSheet("QTableWidget::item { padding: 0px; }");
|
||||
ui.XformColorIndexTable->setStyleSheet("QTableWidget::item { padding: 1px; }");
|
||||
ui.XformColorValuesTable->setStyleSheet("QTableWidget::item { padding: 1px; }");
|
||||
ui.XformPaletteRefTable->setStyleSheet("QTableWidget::item { padding: 0px; border: none; margin: 0px; }");
|
||||
ui.PaletteAdjustTable->setStyleSheet("QTableWidget::item { padding: 1px; }");//Need this to avoid covering the top border pixel with the spinners.
|
||||
ui.statusBar->setStyleSheet("QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }");
|
||||
|
||||
m_PreviousPaletteRow = -1;//Force click handler the first time through.
|
||||
|
||||
//Setup pointer in the GL window to point back to here.
|
||||
ui.GLDisplay->SetMainWindow(this);
|
||||
SetCoordinateStatus(0, 0, 0, 0);
|
||||
|
||||
//At this point, everything has been setup except the renderer. Shortly after
|
||||
//this constructor exits, GLWidget::initializeGL() will create the initial flock and start the rendering timer
|
||||
//which executes whenever the program is idle. Upon starting the timer, the renderer
|
||||
//will be initialized.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor which saves out the settings file.
|
||||
/// All other memory is cleared automatically through the use of STL.
|
||||
/// </summary>
|
||||
Fractorium::~Fractorium()
|
||||
{
|
||||
m_Settings->sync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the render timer and start the delayed restart timer.
|
||||
/// This is a massive hack because Qt has no way of detecting when a resize event
|
||||
/// is started and stopped. Detecting if mouse buttons down is also not an option
|
||||
/// because the documentation says it gives the wrong result.
|
||||
/// </summary>
|
||||
void Fractorium::Resize()
|
||||
{
|
||||
if (!QCoreApplication::closingDown())
|
||||
m_Controller->DelayedStartRenderTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the coordinate text in the status bar.
|
||||
/// </summary>
|
||||
/// <param name="x">The raster x coordinate</param>
|
||||
/// <param name="y">The raster y coordinate</param>
|
||||
/// <param name="worldX">The cartesian world x coordinate</param>
|
||||
/// <param name="worldY">The cartesian world y coordinate</param>
|
||||
void Fractorium::SetCoordinateStatus(int x, int y, float worldX, float worldY)
|
||||
{
|
||||
//Use sprintf rather than allocating and concatenating 6 QStrings for efficiency since this is called on every mouse move.
|
||||
sprintf_s(m_CoordinateString, 128, "Window: %4d, %4d World: %2.2f, %2.2f", x, y, worldX, worldY);
|
||||
m_CoordinateStatusLabel->setText(QString(m_CoordinateString));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the settings for saving an ember to an Xml file to an ember (presumably about to be saved).
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to apply the settings to</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ApplyXmlSavingTemplate(Ember<T>& ember)
|
||||
{
|
||||
ember.m_FinalRasW = m_Fractorium->m_Settings->XmlWidth();
|
||||
ember.m_FinalRasH = m_Fractorium->m_Settings->XmlHeight();
|
||||
ember.m_TemporalSamples = m_Fractorium->m_Settings->XmlTemporalSamples();
|
||||
ember.m_Quality = m_Fractorium->m_Settings->XmlQuality();
|
||||
ember.m_Supersample = m_Fractorium->m_Settings->XmlSupersample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return whether the current ember contains a final xform and the GUI is aware of it.
|
||||
/// </summary>
|
||||
/// <returns>True if the current ember contains a final xform, else false.</returns>
|
||||
bool Fractorium::HaveFinal()
|
||||
{
|
||||
QComboBox* combo = ui.CurrentXformCombo;
|
||||
|
||||
return (combo->count() > 0 && combo->itemText(combo->count() - 1) == "Final");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slots.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Empty placeholder for now.
|
||||
/// Qt has a severe bug where the dock gets hidden behind the window.
|
||||
/// Perhaps this will be used in the future if Qt ever fixes that bug.
|
||||
/// Called when the top level dock is changed.
|
||||
/// </summary>
|
||||
/// <param name="topLevel">True if top level, else false.</param>
|
||||
void Fractorium::OnDockTopLevelChanged(bool topLevel)
|
||||
{
|
||||
//if (topLevel)
|
||||
//{
|
||||
// if (ui.DockWidget->y() <= 0)
|
||||
// ui.DockWidget->setGeometry(ui.DockWidget->x(), ui.DockWidget->y() + 100, ui.DockWidget->width(), ui.DockWidget->height());
|
||||
//
|
||||
// ui.DockWidget->setFloating(true);
|
||||
//}
|
||||
//else
|
||||
// ui.DockWidget->setFloating(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty placeholder for now.
|
||||
/// Qt has a severe bug where the dock gets hidden behind the window.
|
||||
/// Perhaps this will be used in the future if Qt ever fixes that bug.
|
||||
/// Called when the dock location is changed.
|
||||
/// </summary>
|
||||
/// <param name="area">The dock widget area</param>
|
||||
void Fractorium::dockLocationChanged(Qt::DockWidgetArea area)
|
||||
{
|
||||
//ui.DockWidget->resize(500, ui.DockWidget->height());
|
||||
//ui.DockWidget->update();
|
||||
//ui.dockWidget->setFloating(true);
|
||||
//ui.dockWidget->setFloating(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Virtual event overrides.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Resize event, just pass to base.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void Fractorium::resizeEvent(QResizeEvent* e)
|
||||
{
|
||||
QMainWindow::resizeEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop rendering and block before exiting.
|
||||
/// Called on program exit.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void Fractorium::closeEvent(QCloseEvent* e)
|
||||
{
|
||||
if (m_Controller.get())
|
||||
{
|
||||
m_Controller->StopRenderTimer(true);//Will wait until fully exited and stopped.
|
||||
m_Controller->StopPreviewRender();
|
||||
}
|
||||
|
||||
if (e)
|
||||
e->accept();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Examine the files dragged when it first enters the window area.
|
||||
/// Ok if at least one file is .flam3, .flam3 or .xml, else not ok.
|
||||
/// Called when the user first drags files in.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void Fractorium::dragEnterEvent(QDragEnterEvent* e)
|
||||
{
|
||||
if (e->mimeData()->hasUrls())
|
||||
{
|
||||
foreach (QUrl url, e->mimeData()->urls())
|
||||
{
|
||||
QString localFile = url.toLocalFile();
|
||||
QFileInfo fileInfo(localFile);
|
||||
QString suf = fileInfo.suffix();
|
||||
|
||||
if (suf == "flam3" || suf == "flame" || suf == "xml")
|
||||
{
|
||||
e->accept();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always accept drag when moving, so that the drop event will correctly be called.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void Fractorium::dragMoveEvent(QDragMoveEvent* e)
|
||||
{
|
||||
e->accept();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Examine and open the dropped files.
|
||||
/// Called when the user drops a file in.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void Fractorium::dropEvent(QDropEvent* e)
|
||||
{
|
||||
QStringList filenames;
|
||||
Qt::KeyboardModifiers mod = e->keyboardModifiers();
|
||||
bool append = mod.testFlag(Qt::ControlModifier) ? true : false;
|
||||
|
||||
if (e->mimeData()->hasUrls())
|
||||
{
|
||||
foreach (QUrl url, e->mimeData()->urls())
|
||||
{
|
||||
QString localFile = url.toLocalFile();
|
||||
QFileInfo fileInfo(localFile);
|
||||
QString suf = fileInfo.suffix();
|
||||
|
||||
if (suf == "flam3" || suf == "flame" || suf == "xml")
|
||||
filenames << localFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (!filenames.empty())
|
||||
m_Controller->OpenAndPrepFiles(filenames, append);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup a combo box to be placed in a table cell.
|
||||
/// </summary>
|
||||
/// <param name="table">The table the combo box belongs to</param>
|
||||
/// <param name="receiver">The receiver object</param>
|
||||
/// <param name="row">The row in the table where this combo box resides</param>
|
||||
/// <param name="col">The col in the table where this combo box resides</param>
|
||||
/// <param name="comboBox">Double pointer to combo box which will hold the spinner upon exit</param>
|
||||
/// <param name="vals">The string values to populate the combo box with</param>
|
||||
/// <param name="signal">The signal the combo box emits</param>
|
||||
/// <param name="slot">The slot to receive the signal</param>
|
||||
/// <param name="connectionType">Type of the connection. Default: Qt::QueuedConnection.</param>
|
||||
void Fractorium::SetupCombo(QTableWidget* table, const QObject* receiver, int& row, int col, StealthComboBox*& comboBox, vector<string>& vals, const char* signal, const char* slot, Qt::ConnectionType connectionType)
|
||||
{
|
||||
comboBox = new StealthComboBox(table);
|
||||
std::for_each(vals.begin(), vals.end(), [&](string s) { comboBox->addItem(s.c_str()); });
|
||||
table->setCellWidget(row, col, comboBox);
|
||||
connect(comboBox, signal, receiver, slot, connectionType);
|
||||
row++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the header of a table to be fixed.
|
||||
/// </summary>
|
||||
/// <param name="header">The header to set</param>
|
||||
/// <param name="mode">The resizing mode to use. Default: QHeaderView::Fixed.</param>
|
||||
void Fractorium::SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMode mode)
|
||||
{
|
||||
header->setVisible(true);//For some reason, the designer keeps clobbering this value, so force it here.
|
||||
header->setSectionsClickable(false);
|
||||
header->setSectionResizeMode(mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup and show the open XML dialog.
|
||||
/// This will perform lazy instantiation.
|
||||
/// </summary>
|
||||
/// <returns>The filename selected</returns>
|
||||
QStringList Fractorium::SetupOpenXmlDialog()
|
||||
{
|
||||
//Lazy instantiate since it takes a long time.
|
||||
if (!m_FileDialog)
|
||||
{
|
||||
m_FileDialog = new QFileDialog(this);
|
||||
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->setDirectory(m_Settings->OpenFolder());
|
||||
m_FileDialog->selectNameFilter(m_Settings->OpenXmlExt());
|
||||
|
||||
if (m_FileDialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
filenames = m_FileDialog->selectedFiles();
|
||||
|
||||
if (!filenames.empty())
|
||||
m_Settings->OpenFolder(QFileInfo(filenames[0]).canonicalPath());
|
||||
}
|
||||
|
||||
return filenames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup and show the save XML dialog.
|
||||
/// This will perform lazy instantiation.
|
||||
/// </summary>
|
||||
/// <param name="defaultFilename">The default filename to populate the text box with</param>
|
||||
/// <returns>The filename selected</returns>
|
||||
QString Fractorium::SetupSaveXmlDialog(QString defaultFilename)
|
||||
{
|
||||
//Lazy instantiate since it takes a long time.
|
||||
if (!m_FileDialog)
|
||||
{
|
||||
m_FileDialog = new QFileDialog(this);
|
||||
m_FileDialog->setViewMode(QFileDialog::List);
|
||||
}
|
||||
|
||||
if (!m_FileDialog)
|
||||
return "";
|
||||
|
||||
QString filename;
|
||||
|
||||
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
|
||||
connect(m_FileDialog, &QFileDialog::filterSelected, [=](const QString &filter) { m_Settings->SaveXmlExt(filter); });
|
||||
|
||||
//This must come first because it clears various internal states which allow the file text to be properly set.
|
||||
//This is most likely a bug in QFileDialog.
|
||||
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
|
||||
m_FileDialog->selectFile(defaultFilename);
|
||||
m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)");
|
||||
m_FileDialog->setWindowTitle("Save flame as xml");
|
||||
m_FileDialog->setDirectory(m_Settings->SaveFolder());
|
||||
m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt());
|
||||
|
||||
if (m_FileDialog->exec() == QDialog::Accepted)
|
||||
filename = m_FileDialog->selectedFiles().value(0);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup and show the save image dialog.
|
||||
/// This will perform lazy instantiation.
|
||||
/// </summary>
|
||||
/// <param name="defaultFilename">The default filename to populate the text box with</param>
|
||||
/// <returns>The filename selected</returns>
|
||||
QString Fractorium::SetupSaveImageDialog(QString defaultFilename)
|
||||
{
|
||||
//Lazy instantiate since it takes a long time.
|
||||
if (!m_FileDialog)
|
||||
{
|
||||
m_FileDialog = new QFileDialog(this);
|
||||
m_FileDialog->setViewMode(QFileDialog::List);
|
||||
}
|
||||
|
||||
if (!m_FileDialog)
|
||||
return "";
|
||||
|
||||
QString filename;
|
||||
|
||||
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
|
||||
connect(m_FileDialog, &QFileDialog::filterSelected, [=](const QString &filter) { m_Settings->SaveImageExt(filter); });
|
||||
|
||||
//This must come first because it clears various internal states which allow the file text to be properly set.
|
||||
//This is most likely a bug in QFileDialog.
|
||||
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
|
||||
m_FileDialog->selectFile(defaultFilename);
|
||||
m_FileDialog->setFileMode(QFileDialog::AnyFile);
|
||||
m_FileDialog->setOption(QFileDialog::ShowDirsOnly, false);
|
||||
m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, false);
|
||||
m_FileDialog->setNameFilter("Jpeg (*.jpg);;Png (*.png);;Bmp (*.bmp)");
|
||||
m_FileDialog->setWindowTitle("Save image");
|
||||
m_FileDialog->setDirectory(m_Settings->SaveFolder());
|
||||
m_FileDialog->selectNameFilter(m_Settings->SaveImageExt());
|
||||
|
||||
if (m_FileDialog->exec() == QDialog::Accepted)
|
||||
filename = m_FileDialog->selectedFiles().value(0);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup and show the save folder dialog.
|
||||
/// This will perform lazy instantiation.
|
||||
/// </summary>
|
||||
/// <returns>The folder selected</returns>
|
||||
QString Fractorium::SetupSaveFolderDialog()
|
||||
{
|
||||
//Lazy instantiate since it takes a long time.
|
||||
if (!m_FolderDialog)
|
||||
{
|
||||
m_FolderDialog = new QFileDialog(this);
|
||||
m_FolderDialog->setViewMode(QFileDialog::List);
|
||||
}
|
||||
|
||||
if (!m_FolderDialog)
|
||||
return "";
|
||||
|
||||
QString filename;
|
||||
|
||||
//This must come first because it clears various internal states which allow the file text to be properly set.
|
||||
//This is most likely a bug in QFileDialog.
|
||||
m_FolderDialog->setAcceptMode(QFileDialog::AcceptSave);
|
||||
m_FolderDialog->setFileMode(QFileDialog::Directory);
|
||||
m_FolderDialog->setOption(QFileDialog::ShowDirsOnly, true);
|
||||
m_FolderDialog->setOption(QFileDialog::DontUseNativeDialog, true);
|
||||
m_FolderDialog->selectFile("");
|
||||
m_FolderDialog->setWindowTitle("Save to folder");
|
||||
m_FolderDialog->setDirectory(m_Settings->SaveFolder());
|
||||
|
||||
if (m_FolderDialog->exec() == QDialog::Accepted)
|
||||
filename = m_FolderDialog->selectedFiles().value(0);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is no longer needed and was used to compensate for a different bug
|
||||
/// however the code is interesting, so keep it around for possible future use.
|
||||
/// This was used to correct a rotation bug where matrix rotation comes out in the wrong direction
|
||||
/// if x1, y1 (a & d) are on the left side of the line from 0,0 to
|
||||
/// x2, y2 (b, e). In that case, the angle must be flipped. In order
|
||||
/// to determine which side of the line it's on, create a mat2
|
||||
/// and find its determinant. Values > 0 are on the left side of the line.
|
||||
/// </summary>
|
||||
/// <param name="affine">The affine.</param>
|
||||
/// <returns></returns>
|
||||
int Fractorium::FlipDet(Affine2D<float>& affine)
|
||||
{
|
||||
float x1 = affine.A();
|
||||
float y1 = affine.D();
|
||||
float x2 = affine.B();
|
||||
float y2 = affine.E();
|
||||
|
||||
//Just make the other end of the line be the center of the circle.
|
||||
glm::mat2 mat( 0 - x1, 0 - y1,//Col 0.
|
||||
x2 - x1, y2 - y1);//Col 1.
|
||||
|
||||
return (glm::determinant(mat) > 0) ? -1 : 1;
|
||||
}
|
||||
|
||||
//template<typename spinType, typename valType>//See note at the end of Fractorium.h
|
||||
//void Fractorium::SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow, valType val, valType doubleClickZero, valType doubleClickNonZero)
|
||||
//{
|
||||
// spinBox = new spinType(table, height, step);
|
||||
// spinBox->setRange(min, max);
|
||||
// spinBox->setValue(val);
|
||||
// table->setCellWidget(row, col, spinBox);
|
||||
//
|
||||
// if (string(signal) != "" && string(slot) != "")
|
||||
// connect(spinBox, signal, receiver, slot, connectionType);
|
||||
//
|
||||
// if (doubleClickNonZero != -999 && doubleClickZero != -999)
|
||||
// {
|
||||
// spinBox->DoubleClick(true);
|
||||
// spinBox->DoubleClickZero((valType)doubleClickZero);
|
||||
// spinBox->DoubleClickNonZero((valType)doubleClickNonZero);
|
||||
// }
|
||||
//
|
||||
// if (incRow)
|
||||
// row++;
|
||||
//}
|
445
Source/Fractorium/Fractorium.h
Normal file
@ -0,0 +1,445 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_Fractorium.h"
|
||||
#include "GLWidget.h"
|
||||
#include "EmberTreeWidgetItem.h"
|
||||
#include "VariationTreeWidgetItem.h"
|
||||
#include "StealthComboBox.h"
|
||||
#include "TableWidget.h"
|
||||
#include "FinalRenderDialog.h"
|
||||
#include "OptionsDialog.h"
|
||||
#include "AboutDialog.h"
|
||||
|
||||
/// <summary>
|
||||
/// Fractorium class.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Fractorium is the main window for the interactive renderer. The main viewable area
|
||||
/// is a derivation of QGLWidget named GLWidget. The design uses the concept of a controller
|
||||
/// to allow for both polymorphism and templating to coexist. Most processing functionality
|
||||
/// is contained within the controller, and the GUI events just call the appropriate controller
|
||||
/// member functions.
|
||||
/// The rendering takes place on a timer with
|
||||
/// a period of 0 which means it will trigger an event whenever the event input queue is idle.
|
||||
/// As it's rendering, if the user changes anything on the GUI, a re-render will trigger. Since
|
||||
/// certain parameters don't require a full render, the minimum necessary processing will be ran.
|
||||
/// When the user changes something on the GUI, the required processing action is added to a vector.
|
||||
/// Upon the next execution of the idle timer function, the most significant action will be extracted
|
||||
/// and applied to the renderer. The vector is then cleared.
|
||||
/// On the left side of the window is a dock widget which contains all controls needed for
|
||||
/// manipulating embers.
|
||||
/// Qt takes very long to create file dialog windows, so they are kept as members and initialized
|
||||
/// upon first use with lazy instantiation and then kept around for the remainder of the program.
|
||||
/// Additional dialogs are for the about box, options, and final rendering out to a file.
|
||||
/// While all class member variables and functions are declared in this .h file, the implementation
|
||||
/// for them is far too lengthy to put in a single .cpp file. So general functionality is placed in
|
||||
/// Fractorium.cpp and the other functional areas are each broken out into their own files.
|
||||
/// The order of the functions in each .cpp file should roughly match the order they appear in the .h file.
|
||||
/// Future todo list:
|
||||
/// Add all of the plugins/variations that work with Apophysis and are open source.
|
||||
/// Allow specifying variations to include/exclude from random generation.
|
||||
/// Allow the option to specify a different palette file rather than the default flam3-palettes.xml.
|
||||
/// Implement more rendering types.
|
||||
/// Add support for animation previewing.
|
||||
/// Add support for full animation editing and rendering.
|
||||
/// Possibly add some features from JWildFire.
|
||||
/// </summary>
|
||||
class Fractorium : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend GLWidget;
|
||||
friend FractoriumOptionsDialog;
|
||||
friend FractoriumFinalRenderDialog;
|
||||
friend FractoriumAboutDialog;
|
||||
friend GLEmberControllerBase;
|
||||
friend GLEmberController<float>;
|
||||
friend GLEmberController<double>;
|
||||
friend FractoriumEmberControllerBase;
|
||||
friend FractoriumEmberController<float>;
|
||||
friend FractoriumEmberController<double>;
|
||||
friend FinalRenderEmberControllerBase;
|
||||
friend FinalRenderEmberController<float>;
|
||||
friend FinalRenderEmberController<double>;
|
||||
|
||||
public:
|
||||
Fractorium(QWidget* parent = 0);
|
||||
~Fractorium();
|
||||
|
||||
//Geometry.
|
||||
void SetCenter(float x, float y);
|
||||
void SetRotation(double rot, bool stealth);
|
||||
void SetScale(double scale);
|
||||
void Resize();
|
||||
void SetCoordinateStatus(int x, int y, float worldX, float worldY);
|
||||
|
||||
//Xforms.
|
||||
void CurrentXform(unsigned int i);
|
||||
|
||||
//Xforms Affine.
|
||||
bool DrawAllPre();
|
||||
bool DrawAllPost();
|
||||
bool LocalPivot();
|
||||
|
||||
public slots:
|
||||
//Dock.
|
||||
void OnDockTopLevelChanged(bool topLevel);
|
||||
void dockLocationChanged(Qt::DockWidgetArea area);
|
||||
|
||||
//Menu.
|
||||
void OnActionNewFlock(bool checked);//File.
|
||||
void OnActionNewEmptyFlameInCurrentFile(bool checked);
|
||||
void OnActionNewRandomFlameInCurrentFile(bool checked);
|
||||
void OnActionCopyFlameInCurrentFile(bool checked);
|
||||
void OnActionOpen(bool checked);
|
||||
void OnActionSaveCurrentAsXml(bool checked);
|
||||
void OnActionSaveEntireFileAsXml(bool checked);
|
||||
void OnActionSaveCurrentScreen(bool checked);
|
||||
void OnActionSaveCurrentToOpenedFile(bool checked);
|
||||
void OnActionExit(bool checked);
|
||||
|
||||
void OnActionUndo(bool checked);//Edit.
|
||||
void OnActionRedo(bool checked);
|
||||
void OnActionCopyXml(bool checked);
|
||||
void OnActionCopyAllXml(bool checked);
|
||||
void OnActionPasteXmlAppend(bool checked);
|
||||
void OnActionPasteXmlOver(bool checked);
|
||||
|
||||
void OnActionAddReflectiveSymmetry(bool checked);//Tools.
|
||||
void OnActionAddRotationalSymmetry(bool checked);
|
||||
void OnActionAddBothSymmetry(bool checked);
|
||||
void OnActionClearFlame(bool checked);
|
||||
void OnActionRenderPreviews(bool checked);
|
||||
void OnActionStopRenderingPreviews(bool checked);
|
||||
void OnActionFinalRender(bool checked);
|
||||
void OnFinalRenderClose(int result);
|
||||
void OnActionOptions(bool checked);
|
||||
void OnActionAbout(bool checked);//Help.
|
||||
|
||||
//Toolbar.
|
||||
void OnSaveCurrentAsXmlButtonClicked(bool checked);
|
||||
void OnSaveEntireFileAsXmlButtonClicked(bool checked);
|
||||
void OnSaveCurrentToOpenedFileButtonClicked(bool checked);
|
||||
|
||||
//Params.
|
||||
void OnBrightnessChanged(double d);//Color.
|
||||
void OnGammaChanged(double d);
|
||||
void OnGammaThresholdChanged(double d);
|
||||
void OnVibrancyChanged(double d);
|
||||
void OnHighlightPowerChanged(double d);
|
||||
void OnBackgroundColorButtonClicked(bool checked);
|
||||
void OnColorSelected(const QColor& color);
|
||||
void OnPaletteModeComboCurrentIndexChanged(int index);
|
||||
void OnWidthChanged(int d);//Geometry.
|
||||
void OnHeightChanged(int d);
|
||||
void OnCenterXChanged(double d);
|
||||
void OnCenterYChanged(double d);
|
||||
void OnScaleChanged(double d);
|
||||
void OnZoomChanged(double d);
|
||||
void OnRotateChanged(double d);
|
||||
void OnZPosChanged(double d);
|
||||
void OnPerspectiveChanged(double d);
|
||||
void OnPitchChanged(double d);
|
||||
void OnYawChanged(double d);
|
||||
void OnDepthBlurChanged(double d);
|
||||
void OnSpatialFilterWidthChanged(double d);//Filter.
|
||||
void OnSpatialFilterTypeComboCurrentIndexChanged(const QString& text);
|
||||
void OnTemporalFilterWidthChanged(double d);
|
||||
void OnTemporalFilterTypeComboCurrentIndexChanged(const QString& text);
|
||||
void OnDEFilterMinRadiusWidthChanged(double d);
|
||||
void OnDEFilterMaxRadiusWidthChanged(double d);
|
||||
void OnDEFilterCurveWidthChanged(double d);
|
||||
void OnPassesChanged(int d);//Iteration.
|
||||
void OnTemporalSamplesChanged(int d);
|
||||
void OnQualityChanged(double d);
|
||||
void OnSupersampleChanged(int d);
|
||||
void OnAffineInterpTypeComboCurrentIndexChanged(int index);
|
||||
void OnInterpTypeComboCurrentIndexChanged(int index);
|
||||
|
||||
//Xforms.
|
||||
void OnCurrentXformComboChanged(int index);
|
||||
void OnAddXformButtonClicked(bool checked);
|
||||
void OnDuplicateXformButtonClicked(bool checked);
|
||||
void OnClearXformButtonClicked(bool checked);
|
||||
void OnDeleteXformButtonClicked(bool checked);
|
||||
void OnAddFinalXformButtonClicked(bool checked);
|
||||
void OnXformWeightChanged(double d);
|
||||
void OnEqualWeightButtonClicked(bool checked);
|
||||
void OnXformNameChanged(int row, int col);
|
||||
|
||||
//Xforms Affine.
|
||||
void OnX1Changed(double d);
|
||||
void OnX2Changed(double d);
|
||||
void OnY1Changed(double d);
|
||||
void OnY2Changed(double d);
|
||||
void OnO1Changed(double d);
|
||||
void OnO2Changed(double d);
|
||||
|
||||
void OnFlipHorizontalButtonClicked(bool checked);
|
||||
void OnFlipVerticalButtonClicked(bool checked);
|
||||
void OnRotate90CButtonClicked(bool checked);
|
||||
void OnRotate90CcButtonClicked(bool checked);
|
||||
void OnRotateCButtonClicked(bool checked);
|
||||
void OnRotateCcButtonClicked(bool checked);
|
||||
void OnMoveUpButtonClicked(bool checked);
|
||||
void OnMoveDownButtonClicked(bool checked);
|
||||
void OnMoveLeftButtonClicked(bool checked);
|
||||
void OnMoveRightButtonClicked(bool checked);
|
||||
void OnScaleDownButtonClicked(bool checked);
|
||||
void OnScaleUpButtonClicked(bool checked);
|
||||
void OnResetAffineButtonClicked(bool checked);
|
||||
|
||||
void OnAffineGroupBoxToggled(bool on);
|
||||
void OnAffineDrawAllCurrentRadioButtonToggled(bool checked);
|
||||
|
||||
//Xforms Color.
|
||||
void OnXformColorIndexChanged(double d);
|
||||
void OnXformColorIndexChanged(double d, bool updateRender);
|
||||
void OnXformScrollColorIndexChanged(int d);
|
||||
void OnXformColorSpeedChanged(double d);
|
||||
void OnXformOpacityChanged(double d);
|
||||
void OnXformDirectColorChanged(double d);
|
||||
void OnSoloXformCheckBoxStateChanged(int state);
|
||||
void OnXformRefPaletteResized(int logicalIndex, int oldSize, int newSize);
|
||||
|
||||
//Xforms Variations.
|
||||
void OnVariationSpinBoxValueChanged(double d);
|
||||
void OnTreeHeaderSectionClicked(int);
|
||||
void OnVariationsFilterLineEditTextChanged(const QString& text);
|
||||
void OnVariationsFilterClearButtonClicked(bool checked);
|
||||
|
||||
//Xforms Xaos.
|
||||
void OnXaosChanged(double d);
|
||||
void OnXaosFromToToggled(bool checked);
|
||||
void OnClearXaosButtonClicked(bool checked);
|
||||
|
||||
//Palette.
|
||||
void OnPaletteAdjust(int d);
|
||||
void OnPaletteCellClicked(int row, int col);
|
||||
void OnPaletteCellDoubleClicked(int row, int col);
|
||||
|
||||
//Library.
|
||||
void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col);
|
||||
void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col);
|
||||
|
||||
//Rendering/progress.
|
||||
void StartRenderTimer();
|
||||
void IdleTimer();
|
||||
bool ControllersOk();
|
||||
|
||||
//Can't have a template function be a slot.
|
||||
void SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector<unsigned char>& v, unsigned int width, unsigned int height);
|
||||
|
||||
public:
|
||||
//template<typename spinType, typename valType>//See below.
|
||||
//static void SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow = true, valType val = 0, valType doubleClickZero = -999, valType doubleClickNonZero = -999);
|
||||
static void SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, DoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot);
|
||||
static void SetupCombo(QTableWidget* table, const QObject* receiver, int& row, int col, StealthComboBox*& comboBox, vector<string>& vals, const char* signal, const char* slot, Qt::ConnectionType connectionType = Qt::QueuedConnection);
|
||||
static void SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMode mode = QHeaderView::Fixed);
|
||||
static int FlipDet(Affine2D<float>& affine);
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent* e);
|
||||
virtual void closeEvent(QCloseEvent* e);
|
||||
virtual void dragEnterEvent(QDragEnterEvent* e);
|
||||
virtual void dragMoveEvent(QDragMoveEvent* e);
|
||||
virtual void dropEvent(QDropEvent* e);
|
||||
|
||||
private:
|
||||
void InitMenusUI();
|
||||
void InitToolbarUI();
|
||||
void InitParamsUI();
|
||||
void InitXformsUI();
|
||||
void InitXformsColorUI();
|
||||
void InitXformsAffineUI();
|
||||
void InitXformsVariationsUI();
|
||||
void InitXformsXaosUI();
|
||||
void InitPaletteUI();
|
||||
void InitLibraryUI();
|
||||
|
||||
//Embers.
|
||||
bool HaveFinal();
|
||||
|
||||
//Params.
|
||||
|
||||
//Xforms.
|
||||
void FillXforms();
|
||||
|
||||
//Xforms Color.
|
||||
|
||||
//Xforms Affine.
|
||||
|
||||
//Xforms Variations.
|
||||
|
||||
//Xforms Xaos.
|
||||
void FillXaosTable();
|
||||
|
||||
//Palette.
|
||||
void ResetPaletteControls();
|
||||
|
||||
//Library.
|
||||
|
||||
//Info.
|
||||
void UpdateHistogramBounds();
|
||||
void ErrorReportToQTextEdit(vector<string>& errors, QTextEdit* textEdit, bool clear = true);
|
||||
|
||||
//Rendering/progress.
|
||||
bool CreateRendererFromOptions();
|
||||
bool CreateControllerFromOptions();
|
||||
|
||||
//Dialogs.
|
||||
QStringList SetupOpenXmlDialog();
|
||||
QString SetupSaveXmlDialog(QString defaultFilename);
|
||||
QString SetupSaveImageDialog(QString defaultFilename);
|
||||
QString SetupSaveFolderDialog();
|
||||
QColorDialog* m_ColorDialog;
|
||||
FractoriumFinalRenderDialog* m_FinalRenderDialog;
|
||||
FractoriumOptionsDialog* m_OptionsDialog;
|
||||
FractoriumAboutDialog* m_AboutDialog;
|
||||
|
||||
//Params.
|
||||
DoubleSpinBox* m_BrightnessSpin;//Color.
|
||||
DoubleSpinBox* m_GammaSpin;
|
||||
DoubleSpinBox* m_GammaThresholdSpin;
|
||||
DoubleSpinBox* m_VibrancySpin;
|
||||
DoubleSpinBox* m_HighlightSpin;
|
||||
QPushButton* m_BackgroundColorButton;
|
||||
StealthComboBox* m_PaletteModeCombo;
|
||||
SpinBox* m_WidthSpin;//Geometry.
|
||||
SpinBox* m_HeightSpin;
|
||||
DoubleSpinBox* m_CenterXSpin;
|
||||
DoubleSpinBox* m_CenterYSpin;
|
||||
DoubleSpinBox* m_ScaleSpin;
|
||||
DoubleSpinBox* m_ZoomSpin;
|
||||
DoubleSpinBox* m_RotateSpin;
|
||||
DoubleSpinBox* m_ZPosSpin;
|
||||
DoubleSpinBox* m_PerspectiveSpin;
|
||||
DoubleSpinBox* m_PitchSpin;
|
||||
DoubleSpinBox* m_YawSpin;
|
||||
DoubleSpinBox* m_DepthBlurSpin;
|
||||
DoubleSpinBox* m_SpatialFilterWidthSpin;//Filter.
|
||||
StealthComboBox* m_SpatialFilterTypeCombo;
|
||||
DoubleSpinBox* m_TemporalFilterWidthSpin;
|
||||
StealthComboBox* m_TemporalFilterTypeCombo;
|
||||
DoubleSpinBox* m_DEFilterMinRadiusSpin;
|
||||
DoubleSpinBox* m_DEFilterMaxRadiusSpin;
|
||||
DoubleSpinBox* m_DECurveSpin;
|
||||
SpinBox* m_PassesSpin;//Iteration.
|
||||
SpinBox* m_TemporalSamplesSpin;
|
||||
DoubleSpinBox* m_QualitySpin;
|
||||
SpinBox* m_SupersampleSpin;
|
||||
StealthComboBox* m_AffineInterpTypeCombo;
|
||||
StealthComboBox* m_InterpTypeCombo;
|
||||
|
||||
//Xforms.
|
||||
DoubleSpinBox* m_XformWeightSpin;
|
||||
SpinnerButtonWidget* m_XformWeightSpinnerButtonWidget;
|
||||
|
||||
//Xforms Color.
|
||||
QTableWidgetItem* m_XformColorValueItem;
|
||||
QTableWidgetItem* m_PaletteRefItem;
|
||||
DoubleSpinBox* m_XformColorIndexSpin;
|
||||
DoubleSpinBox* m_XformColorSpeedSpin;
|
||||
DoubleSpinBox* m_XformOpacitySpin;
|
||||
DoubleSpinBox* m_XformDirectColorSpin;
|
||||
|
||||
//Xforms Affine.
|
||||
DoubleSpinBox* m_PreX1Spin;//Pre.
|
||||
DoubleSpinBox* m_PreX2Spin;
|
||||
DoubleSpinBox* m_PreY1Spin;
|
||||
DoubleSpinBox* m_PreY2Spin;
|
||||
DoubleSpinBox* m_PreO1Spin;
|
||||
DoubleSpinBox* m_PreO2Spin;
|
||||
|
||||
DoubleSpinBox* m_PostX1Spin;//Post.
|
||||
DoubleSpinBox* m_PostX2Spin;
|
||||
DoubleSpinBox* m_PostY1Spin;
|
||||
DoubleSpinBox* m_PostY2Spin;
|
||||
DoubleSpinBox* m_PostO1Spin;
|
||||
DoubleSpinBox* m_PostO2Spin;
|
||||
|
||||
//Palette.
|
||||
SpinBox* m_PaletteHueSpin;
|
||||
SpinBox* m_PaletteSaturationSpin;
|
||||
SpinBox* m_PaletteBrightnessSpin;
|
||||
SpinBox* m_PaletteContrastSpin;
|
||||
SpinBox* m_PaletteBlurSpin;
|
||||
SpinBox* m_PaletteFrequencySpin;
|
||||
|
||||
//Files.
|
||||
QFileDialog* m_FileDialog;
|
||||
QFileDialog* m_FolderDialog;
|
||||
QString m_LastSaveAll;
|
||||
QString m_LastSaveCurrent;
|
||||
//QMenu* m_FileTreeMenu;
|
||||
|
||||
QProgressBar* m_ProgressBar;
|
||||
QLabel* m_RenderStatusLabel;
|
||||
QLabel* m_CoordinateStatusLabel;
|
||||
FractoriumSettings* m_Settings;
|
||||
char m_ULString[32];
|
||||
char m_URString[32];
|
||||
char m_LRString[32];
|
||||
char m_LLString[32];
|
||||
char m_WString[16];
|
||||
char m_HString[16];
|
||||
char m_DEString[16];
|
||||
char m_CoordinateString[128];
|
||||
|
||||
int m_FontSize;
|
||||
int m_VarSortMode;
|
||||
int m_PreviousPaletteRow;
|
||||
OpenCLWrapper m_Wrapper;
|
||||
auto_ptr<FractoriumEmberControllerBase> m_Controller;
|
||||
Ui::FractoriumClass ui;
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Setup a spinner to be placed in a table cell.
|
||||
/// Due to a serious compiler bug in MSVC, this must be declared as an outside function instead of a static member of Fractorium.
|
||||
/// The reason is that the default arguments of type valType will not be interpreted correctly by the compiler.
|
||||
/// If the bug is ever fixed, put it back as a static member function.
|
||||
/// </summary>
|
||||
/// <param name="table">The table the spinner belongs to</param>
|
||||
/// <param name="receiver">The receiver object</param>
|
||||
/// <param name="row">The row in the table where this spinner resides</param>
|
||||
/// <param name="col">The col in the table where this spinner resides</param>
|
||||
/// <param name="spinBox">Double pointer to spin box which will hold the spinner upon exit</param>
|
||||
/// <param name="height">The height of the spinner</param>
|
||||
/// <param name="min">The minimum value of the spinner</param>
|
||||
/// <param name="max">The maximum value of the spinner</param>
|
||||
/// <param name="step">The step of the spinner</param>
|
||||
/// <param name="signal">The signal the spinner emits</param>
|
||||
/// <param name="slot">The slot to receive the signal</param>
|
||||
/// <param name="incRow">Whether to increment the row value</param>
|
||||
/// <param name="val">The default value for the spinner</param>
|
||||
/// <param name="doubleClickZero">When the spinner has a value of zero and is double clicked, assign this value</param>
|
||||
/// <param name="doubleClickNonZero">When the spinner has a value of non-zero and is double clicked, assign this value</param>
|
||||
template<typename spinType, typename valType>
|
||||
static void SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow = true, valType val = 0, valType doubleClickZero = -999, valType doubleClickNonZero = -999)
|
||||
{
|
||||
spinBox = new spinType(table, height, step);
|
||||
spinBox->setRange(min, max);
|
||||
spinBox->setValue(val);
|
||||
|
||||
if (col >= 0)
|
||||
table->setCellWidget(row, col, spinBox);
|
||||
|
||||
if (string(signal) != "" && string(slot) != "")
|
||||
receiver->connect(spinBox, signal, receiver, slot, Qt::QueuedConnection);
|
||||
|
||||
if (doubleClickNonZero != -999 && doubleClickZero != -999)
|
||||
{
|
||||
spinBox->DoubleClick(true);
|
||||
spinBox->DoubleClickZero((valType)doubleClickZero);
|
||||
spinBox->DoubleClickNonZero((valType)doubleClickNonZero);
|
||||
}
|
||||
|
||||
if (incRow)
|
||||
row++;
|
||||
}
|
||||
|
||||
//template void Fractorium::SetupSpinner<SpinBox, int> (QTableWidget* table, const QObject* receiver, int& row, int col, SpinBox*& spinBox, int height, int min, int max, int step, const char* signal, const char* slot, bool incRow, int val, int doubleClickZero, int doubleClickNonZero);
|
||||
//template void Fractorium::SetupSpinner<DoubleSpinBox, double>(QTableWidget* table, const QObject* receiver, int& row, int col, DoubleSpinBox*& spinBox, int height, double min, double max, double step, const char* signal, const char* slot, bool incRow, double val, double doubleClickZero, double doubleClickNonZero);
|
44
Source/Fractorium/Fractorium.qrc
Normal file
@ -0,0 +1,44 @@
|
||||
<RCC>
|
||||
<qresource prefix="/Fractorium">
|
||||
<file>Icons/arrow-undo.png</file>
|
||||
<file>Icons/arrow-redo.png</file>
|
||||
<file>Icons/stop.png</file>
|
||||
<file>Icons/application_side_boxes.png</file>
|
||||
<file>Icons/page_copy.png</file>
|
||||
<file>Icons/page_paste.png</file>
|
||||
<file>Icons/window-close.png</file>
|
||||
<file>Icons/068123-3d-transparent-glass-icon-alphanumeric-question-mark3.png</file>
|
||||
<file>Icons/layer--plus.png</file>
|
||||
<file>Icons/layers.png</file>
|
||||
<file>Icons/layers-stack.png</file>
|
||||
<file>Icons/monitor.png</file>
|
||||
<file>Icons/016938-3d-transparent-glass-icon-symbols-shapes-shape-square-clear-16.png</file>
|
||||
<file>Icons/document-hf-insert.png</file>
|
||||
<file>Icons/010425-3d-transparent-glass-icon-animals-spiderweb2.png</file>
|
||||
<file>Icons/database-medium.png</file>
|
||||
<file>Icons/databases.png</file>
|
||||
<file>Icons/drive-harddisk-5.png</file>
|
||||
<file>Icons/folder-visiting-4.png</file>
|
||||
<file>Icons/display-brightness-off.png</file>
|
||||
<file>Icons/cog.png</file>
|
||||
<file>Icons/proxy.png</file>
|
||||
<file>Icons/shape_flip_horizontal.png</file>
|
||||
<file>Icons/shape_flip_vertical.png</file>
|
||||
<file>Icons/arrow_down.png</file>
|
||||
<file>Icons/arrow_in.png</file>
|
||||
<file>Icons/arrow_left.png</file>
|
||||
<file>Icons/arrow_out.png</file>
|
||||
<file>Icons/arrow_right.png</file>
|
||||
<file>Icons/arrow_rotate_anticlockwise.png</file>
|
||||
<file>Icons/arrow_rotate_clockwise.png</file>
|
||||
<file>Icons/arrow_turn_left.png</file>
|
||||
<file>Icons/arrow_turn_right.png</file>
|
||||
<file>Icons/arrow_up.png</file>
|
||||
<file>Icons/configure.png</file>
|
||||
<file>Icons/infomation.png</file>
|
||||
<file>Icons/del.png</file>
|
||||
<file>Icons/add.png</file>
|
||||
<file>Icons/eraser.png</file>
|
||||
<file>Icons/editraise.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
Source/Fractorium/Fractorium.rc
Normal file
5304
Source/Fractorium/Fractorium.ui
Normal file
244
Source/Fractorium/FractoriumEmberController.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "FractoriumEmberController.h"
|
||||
#include "Fractorium.h"
|
||||
#include "GLEmberController.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which initializes the non-templated members contained in this class.
|
||||
/// The renderer, other templated members and GUI setup will be done in the templated derived controller class.
|
||||
/// </summary>
|
||||
/// <param name="fractorium">Pointer to the main window.</param>
|
||||
FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractorium)
|
||||
{
|
||||
Timing t;
|
||||
|
||||
m_Rendering = false;
|
||||
m_Shared = true;
|
||||
m_Platform = 0;
|
||||
m_Device = 0;
|
||||
m_FailedRenders = 0;
|
||||
m_UndoIndex = 0;
|
||||
m_RenderType = CPU_RENDERER;
|
||||
m_OutputTexID = 0;
|
||||
m_SubBatchCount = 1;//Will be ovewritten by the options on first render.
|
||||
m_Fractorium = fractorium;
|
||||
m_RenderTimer = NULL;
|
||||
m_RenderRestartTimer = NULL;
|
||||
m_Rand = QTIsaac<ISAAC_SIZE, ISAAC_INT>(ISAAC_INT(t.Tic()), ISAAC_INT(t.Tic() * 2), ISAAC_INT(t.Tic() * 3));//Ensure a different rand seed on each instance.
|
||||
|
||||
m_RenderTimer = new QTimer(m_Fractorium);
|
||||
m_RenderTimer->setInterval(0);
|
||||
m_Fractorium->connect(m_RenderTimer, SIGNAL(timeout()), SLOT(IdleTimer()));
|
||||
|
||||
m_RenderRestartTimer = new QTimer(m_Fractorium);
|
||||
m_Fractorium->connect(m_RenderRestartTimer, SIGNAL(timeout()), SLOT(StartRenderTimer()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor which stops rendering and deletes the timers.
|
||||
/// All other memory is cleared automatically through the use of STL.
|
||||
/// </summary>
|
||||
FractoriumEmberControllerBase::~FractoriumEmberControllerBase()
|
||||
{
|
||||
StopRenderTimer(true);
|
||||
|
||||
if (m_RenderTimer)
|
||||
{
|
||||
m_RenderTimer->stop();
|
||||
delete m_RenderTimer;
|
||||
m_RenderTimer = NULL;
|
||||
}
|
||||
|
||||
if (m_RenderRestartTimer)
|
||||
{
|
||||
m_RenderRestartTimer->stop();
|
||||
delete m_RenderRestartTimer;
|
||||
m_RenderRestartTimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes the main window parameter to the base, initializes the templated members contained in this class.
|
||||
/// Then sets up the parts of the GUI that require templated Widgets, such as the variations tree and the palette table.
|
||||
/// Note the renderer is not setup here automatically. Instead, it must be manually created by the caller later.
|
||||
/// </summary>
|
||||
/// <param name="fractorium">Pointer to the main window.</param>
|
||||
template <typename T>
|
||||
FractoriumEmberController<T>::FractoriumEmberController(Fractorium* fractorium)
|
||||
: FractoriumEmberControllerBase(fractorium)
|
||||
{
|
||||
m_PreviewRun = false;
|
||||
m_PreviewRunning = false;
|
||||
m_SheepTools = auto_ptr<SheepTools<T, T>>(new SheepTools<T, T>("flam3-palettes.xml", new EmberNs::Renderer<T, T>()));
|
||||
m_GLController = auto_ptr<GLEmberController<T>>(new GLEmberController<T>(fractorium, fractorium->ui.GLDisplay, this));
|
||||
m_PreviewRenderer = auto_ptr<EmberNs::Renderer<T, T>>(new EmberNs::Renderer<T, T>());
|
||||
SetupVariationTree();
|
||||
InitPaletteTable("flam3-palettes.xml");
|
||||
BackgroundChanged(QColor(0, 0, 0));//Default to black.
|
||||
ClearUndo();
|
||||
|
||||
m_PreviewRenderer->Callback(NULL);
|
||||
m_PreviewRenderer->NumChannels(4);
|
||||
m_PreviewRenderer->ReclaimOnResize(true);
|
||||
m_PreviewRenderer->SetEmber(m_Ember);//Give it an initial ember, will be updated many times later.
|
||||
//m_PreviewRenderer->ThreadCount(1);//For debugging.
|
||||
|
||||
m_PreviewRenderFunc = [&](unsigned int start, unsigned int end)
|
||||
{
|
||||
while(m_PreviewRun || m_PreviewRunning)
|
||||
{
|
||||
}
|
||||
|
||||
m_PreviewRun = true;
|
||||
m_PreviewRunning = true;
|
||||
m_PreviewRenderer->ThreadCount(max(1, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
|
||||
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
||||
|
||||
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
||||
{
|
||||
for (size_t i = start; m_PreviewRun && i < end && i < m_EmberFile.m_Embers.size(); i++)
|
||||
{
|
||||
Ember<T> ember = m_EmberFile.m_Embers[i];
|
||||
|
||||
ember.SetSizeAndAdjustScale(PREVIEW_SIZE, PREVIEW_SIZE, false, SCALE_WIDTH);
|
||||
ember.m_TemporalSamples = 1;
|
||||
ember.m_Quality = 25;
|
||||
ember.m_Supersample = 1;
|
||||
m_PreviewRenderer->SetEmber(ember);
|
||||
|
||||
if (m_PreviewRenderer->Run(m_PreviewFinalImage) == RENDER_OK)
|
||||
{
|
||||
if (EmberTreeWidgetItem<T>* treeItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
|
||||
{
|
||||
//It is critical that Qt::BlockingQueuedConnection is passed because this is running on a different thread than the UI.
|
||||
//This ensures the events are processed in order as each preview is updated, and that control does not return here
|
||||
//until the update is complete.
|
||||
QMetaObject::invokeMethod(m_Fractorium, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(EmberTreeWidgetItemBase*, (EmberTreeWidgetItemBase*)treeItem),
|
||||
Q_ARG(vector<unsigned char>&, m_PreviewFinalImage),
|
||||
Q_ARG(unsigned int, PREVIEW_SIZE),
|
||||
Q_ARG(unsigned int, PREVIEW_SIZE));
|
||||
|
||||
//treeItem->SetImage(m_PreviewFinalImage, PREVIEW_SIZE, PREVIEW_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_PreviewRun = false;
|
||||
m_PreviewRunning = false;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty destructor that does nothing.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
FractoriumEmberController<T>::~FractoriumEmberController() { }
|
||||
|
||||
/// <summary>
|
||||
/// Setters for embers, ember files and palettes which convert between float and double types.
|
||||
/// These are used to preserve the current ember/file when switching between renderers.
|
||||
/// Note that some precision will be lost when going from double to float.
|
||||
/// </summary>
|
||||
template <typename T> void FractoriumEmberController<T>::SetEmber(const Ember<float>& ember, bool verbatim) { SetEmberPrivate<float>(ember, verbatim); }
|
||||
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<float>& ember) { ember = m_Ember; }
|
||||
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile) { m_EmberFile = emberFile; }
|
||||
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile) { emberFile = m_EmberFile; }
|
||||
template <typename T> void FractoriumEmberController<T>::SetTempPalette(const Palette<float>& palette) { m_TempPalette = palette; }
|
||||
template <typename T> void FractoriumEmberController<T>::CopyTempPalette(Palette<float>& palette) { palette = m_TempPalette; }
|
||||
#ifdef DO_DOUBLE
|
||||
template <typename T> void FractoriumEmberController<T>::SetEmber(const Ember<double>& ember, bool verbatim) { SetEmberPrivate<double>(ember, verbatim); }
|
||||
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<double>& ember) { ember = m_Ember; }
|
||||
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile) { m_EmberFile = emberFile; }
|
||||
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile) { emberFile = m_EmberFile; }
|
||||
template <typename T> void FractoriumEmberController<T>::SetTempPalette(const Palette<double>& palette) { m_TempPalette = palette; }
|
||||
template <typename T> void FractoriumEmberController<T>::CopyTempPalette(Palette<double>& palette) { palette = m_TempPalette; }
|
||||
#endif
|
||||
template <typename T> Ember<T>* FractoriumEmberController<T>::CurrentEmber() { return &m_Ember; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the ember at the specified index from the currently opened file as the current Ember.
|
||||
/// Clears the undo state.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="index">The index in the file from which to retrieve the ember</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SetEmber(size_t index)
|
||||
{
|
||||
if (index < m_EmberFile.m_Embers.size())
|
||||
{
|
||||
if (QTreeWidgetItem* top = m_Fractorium->ui.LibraryTree->topLevelItem(0))
|
||||
{
|
||||
for (unsigned int i = 0; i < top->childCount(); i++)
|
||||
{
|
||||
if (EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
|
||||
emberItem->setSelected(i == index);
|
||||
}
|
||||
}
|
||||
|
||||
ClearUndo();
|
||||
SetEmber(m_EmberFile.m_Embers[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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="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)
|
||||
{
|
||||
func();
|
||||
|
||||
if (updateRender)
|
||||
UpdateRender(action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper to call a function on the current xform, 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="action">The action to add to the rendering queue. Default: FULL_RENDER.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::UpdateCurrentXform(std::function<void (Xform<T>*)> func, bool updateRender, eProcessAction action)
|
||||
{
|
||||
if (Xform<T>* xform = CurrentXform())
|
||||
{
|
||||
func(xform);
|
||||
|
||||
if (updateRender)
|
||||
UpdateRender(action);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the current ember, but use GUI values for the fields which make sense to
|
||||
/// keep the same between ember selection changes.
|
||||
/// Note the extra template parameter U allows for assigning ember of different types.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to set as the current</param>
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool verbatim)
|
||||
{
|
||||
if (ember.m_Name != m_Ember.m_Name)
|
||||
m_LastSaveCurrent = "";
|
||||
|
||||
m_Ember = ember;
|
||||
|
||||
if (!verbatim)
|
||||
{
|
||||
m_Ember.SetSizeAndAdjustScale(m_Fractorium->ui.GLDisplay->width(), m_Fractorium->ui.GLDisplay->height(), true, SCALE_WIDTH);
|
||||
m_Ember.m_TemporalSamples = 1;//Change once animation is supported.
|
||||
m_Ember.m_Quality = m_Fractorium->m_QualitySpin->value();
|
||||
m_Ember.m_Supersample = m_Fractorium->m_SupersampleSpin->value();
|
||||
}
|
||||
|
||||
m_Fractorium->FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo.
|
||||
FillParamTablesAndPalette();
|
||||
}
|
446
Source/Fractorium/FractoriumEmberController.h
Normal file
@ -0,0 +1,446 @@
|
||||
#pragma once
|
||||
|
||||
#include "EmberFile.h"
|
||||
#include "DoubleSpinBox.h"
|
||||
#include "GLEmberController.h"
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumEmberControllerBase and FractoriumEmberController<T> classes.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// An enum representing the type of edit being done.
|
||||
/// </summary>
|
||||
enum eEditUndoState : unsigned int { REGULAR_EDIT = 0, UNDO_REDO = 1, EDIT_UNDO = 2 };
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumEmberController and Fractorium need each other, but each can't include the other.
|
||||
/// So Fractorium includes this file, and Fractorium is declared as a forward declaration here.
|
||||
/// </summary>
|
||||
class Fractorium;
|
||||
#define PREVIEW_SIZE 256
|
||||
#define UNDO_SIZE 128
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumEmberControllerBase serves as a non-templated base class with virtual
|
||||
/// functions which will be overridden in a derived class that takes a template parameter.
|
||||
/// The controller serves as a way to access both the Fractorium GUI and the underlying ember
|
||||
/// objects through an interface that doesn't require template argument, but does allow
|
||||
/// templated objects to be used underneath.
|
||||
/// Note that there are a few functions which access a templated object, so for those both
|
||||
/// versions for float and double must be provided, then overridden in the templated derived
|
||||
/// class. It's definitely a design flaw, but C++ doesn't offer any alternative since
|
||||
/// templated virtual functions are not supported.
|
||||
/// The functions not implemented in this file can be found in the GUI files which use them.
|
||||
/// </summary>
|
||||
class FractoriumEmberControllerBase : public RenderCallback
|
||||
{
|
||||
public:
|
||||
FractoriumEmberControllerBase(Fractorium* fractorium);
|
||||
virtual ~FractoriumEmberControllerBase();
|
||||
|
||||
//Embers.
|
||||
virtual void SetEmber(const Ember<float>& ember, bool verbatim = false) { }
|
||||
virtual void CopyEmber(Ember<float>& ember) { }
|
||||
virtual void SetEmberFile(const EmberFile<float>& emberFile) { }
|
||||
virtual void CopyEmberFile(EmberFile<float>& emberFile) { }
|
||||
virtual void SetTempPalette(const Palette<float>& palette) { }
|
||||
virtual void CopyTempPalette(Palette<float>& palette) { }
|
||||
#ifdef DO_DOUBLE
|
||||
virtual void SetEmber(const Ember<double>& ember, bool verbatim = false) { }
|
||||
virtual void CopyEmber(Ember<double>& ember) { }
|
||||
virtual void SetEmberFile(const EmberFile<double>& emberFile) { }
|
||||
virtual void CopyEmberFile(EmberFile<double>& emberFile) { }
|
||||
virtual void SetTempPalette(const Palette<double>& palette) { }
|
||||
virtual void CopyTempPalette(Palette<double>& palette) { }
|
||||
#endif
|
||||
virtual void SetEmber(size_t index) { }
|
||||
virtual void Clear() { }
|
||||
virtual void AddXform() { }
|
||||
virtual void DuplicateXform() { }
|
||||
virtual void ClearCurrentXform() { }
|
||||
virtual void DeleteCurrentXform() { }
|
||||
virtual void AddFinalXform() { }
|
||||
virtual bool UseFinalXform() { return false; }
|
||||
virtual size_t XformCount() { return 0; }
|
||||
virtual size_t TotalXformCount() { return 0; }
|
||||
virtual string Name() { return ""; }
|
||||
virtual void Name(string s) { }
|
||||
virtual unsigned int FinalRasW() { return 0; }
|
||||
virtual void FinalRasW(unsigned int w) { }
|
||||
virtual unsigned int FinalRasH() { return 0; }
|
||||
virtual void FinalRasH(unsigned int h) { }
|
||||
virtual void AddSymmetry(int sym, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) { }
|
||||
virtual void CalcNormalizedWeights() { }
|
||||
|
||||
//Menu.
|
||||
virtual void NewFlock(unsigned int count) { }//File.
|
||||
virtual void NewEmptyFlameInCurrentFile() { }
|
||||
virtual void NewRandomFlameInCurrentFile() { }
|
||||
virtual void CopyFlameInCurrentFile() { }
|
||||
virtual void OpenAndPrepFiles(QStringList filenames, bool append) { }
|
||||
virtual void SaveCurrentAsXml() { }
|
||||
virtual void SaveEntireFileAsXml() { }
|
||||
virtual void SaveCurrentToOpenedFile() { }
|
||||
virtual void Undo() { }//Edit.
|
||||
virtual void Redo() { }
|
||||
virtual void CopyXml() { }
|
||||
virtual void CopyAllXml() { }
|
||||
virtual void PasteXmlAppend() { }
|
||||
virtual void PasteXmlOver() { }
|
||||
virtual void AddReflectiveSymmetry() { }//Tools.
|
||||
virtual void AddRotationalSymmetry() { }
|
||||
virtual void AddBothSymmetry() { }
|
||||
virtual void ClearFlame() { }
|
||||
|
||||
//Toolbar.
|
||||
|
||||
//Params.
|
||||
virtual void SetCenter(double x, double y) { }
|
||||
virtual void FillParamTablesAndPalette() { }
|
||||
virtual void BrightnessChanged(double d) { }
|
||||
virtual void GammaChanged(double d) { }
|
||||
virtual void GammaThresholdChanged(double d) { }
|
||||
virtual void VibrancyChanged(double d) { }
|
||||
virtual void HighlightPowerChanged(double d) { }
|
||||
virtual void PaletteModeChanged(unsigned int i) { }
|
||||
virtual void CenterXChanged(double d) { }
|
||||
virtual void CenterYChanged(double d) { }
|
||||
virtual void ScaleChanged(double d) { }
|
||||
virtual void ZoomChanged(double d) { }
|
||||
virtual void RotateChanged(double d) { }
|
||||
virtual void ZPosChanged(double d) { }
|
||||
virtual void PerspectiveChanged(double d) { }
|
||||
virtual void PitchChanged(double d) { }
|
||||
virtual void YawChanged(double d) { }
|
||||
virtual void DepthBlurChanged(double d) { }
|
||||
virtual void SpatialFilterWidthChanged(double d) { }
|
||||
virtual void SpatialFilterTypeChanged(const QString& text) { }
|
||||
virtual void TemporalFilterWidthChanged(double d) { }
|
||||
virtual void TemporalFilterTypeChanged(const QString& text) { }
|
||||
virtual void DEFilterMinRadiusWidthChanged(double d) { }
|
||||
virtual void DEFilterMaxRadiusWidthChanged(double d) { }
|
||||
virtual void DEFilterCurveWidthChanged(double d) { }
|
||||
virtual void PassesChanged(int i) { }
|
||||
virtual void TemporalSamplesChanged(int d) { }
|
||||
virtual void QualityChanged(double d) { }
|
||||
virtual void SupersampleChanged(int d) { }
|
||||
virtual void AffineInterpTypeChanged(int i) { }
|
||||
virtual void InterpTypeChanged(int i) { }
|
||||
virtual void BackgroundChanged(const QColor& color) { }
|
||||
|
||||
//Xforms.
|
||||
virtual void CurrentXformComboChanged(int index) { }
|
||||
virtual void XformWeightChanged(double d) { }
|
||||
virtual void EqualizeWeights() { }
|
||||
virtual void XformNameChanged(int row, int col) { }
|
||||
|
||||
//Xforms Affine.
|
||||
virtual void AffineSetHelper(double d, int index, bool pre) { }
|
||||
virtual void FlipCurrentXform(bool horizontal, bool vertical, bool pre) { }
|
||||
virtual void RotateCurrentXformByAngle(double angle, bool pre) { }
|
||||
virtual void MoveCurrentXform(double x, double y, bool pre) { }
|
||||
virtual void ScaleCurrentXform(double scale, bool pre) { }
|
||||
virtual void ResetCurrentXformAffine(bool pre) { }
|
||||
|
||||
//Xforms Color.
|
||||
virtual void XformColorIndexChanged(double d, bool updateRender) { }
|
||||
virtual void XformScrollColorIndexChanged(int d) { }
|
||||
virtual void XformColorSpeedChanged(double d) { }
|
||||
virtual void XformOpacityChanged(double d) { }
|
||||
virtual void XformDirectColorChanged(double d) { }
|
||||
void SetPaletteRefTable(QPixmap* pixmap);
|
||||
|
||||
//Xforms Variations.
|
||||
virtual void SetupVariationTree() { }
|
||||
virtual void ClearVariationsTree() { }
|
||||
virtual void VariationSpinBoxValueChanged(double d) { }
|
||||
|
||||
//Xforms Xaos.
|
||||
virtual void FillXaosWithCurrentXform() { }
|
||||
virtual QString MakeXaosNameString(unsigned int i) { return ""; }
|
||||
virtual void XaosChanged(DoubleSpinBox* sender) { }
|
||||
virtual void ClearXaos() { }
|
||||
|
||||
//Palette.
|
||||
virtual bool InitPaletteTable(string s) { return false; }
|
||||
virtual void ApplyPaletteToEmber() { }
|
||||
virtual void PaletteAdjust() { }
|
||||
virtual QRgb GetQRgbFromPaletteIndex(unsigned int 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(unsigned int start = UINT_MAX, unsigned int end = UINT_MAX) { }
|
||||
virtual void StopPreviewRender() { }
|
||||
|
||||
//Info.
|
||||
|
||||
//Rendering/progress.
|
||||
virtual bool Render() { return false; }
|
||||
virtual bool CreateRenderer(eRendererType renderType, unsigned int platform, unsigned int device, bool shared = true) { return false; }
|
||||
virtual unsigned int SizeOfT() { return 0; }
|
||||
virtual void ClearUndo() { }
|
||||
virtual GLEmberControllerBase* GLController() { return NULL; }
|
||||
bool RenderTimerRunning();
|
||||
void StartRenderTimer();
|
||||
void DelayedStartRenderTimer();
|
||||
void StopRenderTimer(bool wait);
|
||||
void Shutdown();
|
||||
void UpdateRender(eProcessAction action = FULL_RENDER);
|
||||
void DeleteRenderer();
|
||||
void SaveCurrentRender(QString filename);
|
||||
RendererBase* Renderer() { return m_Renderer.get(); }
|
||||
vector<unsigned char>* FinalImage() { return &m_FinalImage; }
|
||||
vector<unsigned char>* PreviewFinalImage() { return &m_PreviewFinalImage; }
|
||||
|
||||
protected:
|
||||
//Rendering/progress.
|
||||
void AddProcessAction(eProcessAction action);
|
||||
eProcessAction CondenseAndClearProcessActions();
|
||||
eProcessState ProcessState() { return m_Renderer.get() ? m_Renderer->ProcessState() : NONE; }
|
||||
|
||||
//Non-templated members.
|
||||
bool m_Rendering;
|
||||
bool m_Shared;
|
||||
bool m_LastEditWasUndoRedo;
|
||||
unsigned int m_Platform;
|
||||
unsigned int m_Device;
|
||||
unsigned int m_SubBatchCount;
|
||||
unsigned int m_FailedRenders;
|
||||
unsigned int m_UndoIndex;
|
||||
eRendererType m_RenderType;
|
||||
eEditUndoState m_EditState;
|
||||
GLuint m_OutputTexID;
|
||||
Timing m_RenderElapsedTimer;
|
||||
QImage m_FinalPaletteImage;
|
||||
QString m_LastSaveAll;
|
||||
QString m_LastSaveCurrent;
|
||||
CriticalSection m_Cs;
|
||||
vector<unsigned char> m_FinalImage;
|
||||
vector<unsigned char> m_PreviewFinalImage;
|
||||
vector<eProcessAction> m_ProcessActions;
|
||||
auto_ptr<EmberNs::RendererBase> m_Renderer;
|
||||
QTIsaac<ISAAC_SIZE, ISAAC_INT> m_Rand;
|
||||
Fractorium* m_Fractorium;
|
||||
QTimer* m_RenderTimer;
|
||||
QTimer* m_RenderRestartTimer;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Templated derived class which implements all interaction functionality between the embers
|
||||
/// of a specific template type and the GUI.
|
||||
/// Switching between template arguments requires complete re-creation of the controller and the
|
||||
/// underlying renderer. Switching between CPU and OpenCL only requires re-creation of the renderer.
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
class FractoriumEmberController : public FractoriumEmberControllerBase
|
||||
{
|
||||
public:
|
||||
FractoriumEmberController(Fractorium* fractorium);
|
||||
virtual ~FractoriumEmberController();
|
||||
|
||||
//Embers.
|
||||
virtual void SetEmber(const Ember<float>& ember, bool verbatim = false);
|
||||
virtual void CopyEmber(Ember<float>& ember);
|
||||
virtual void SetEmberFile(const EmberFile<float>& emberFile);
|
||||
virtual void CopyEmberFile(EmberFile<float>& emberFile);
|
||||
virtual void SetTempPalette(const Palette<float>& palette);
|
||||
virtual void CopyTempPalette(Palette<float>& palette);
|
||||
#ifdef DO_DOUBLE
|
||||
virtual void SetEmber(const Ember<double>& ember, bool verbatim = false);
|
||||
virtual void CopyEmber(Ember<double>& ember);
|
||||
virtual void SetEmberFile(const EmberFile<double>& emberFile);
|
||||
virtual void CopyEmberFile(EmberFile<double>& emberFile);
|
||||
virtual void SetTempPalette(const Palette<double>& palette);
|
||||
virtual void CopyTempPalette(Palette<double>& palette);
|
||||
#endif
|
||||
virtual void SetEmber(size_t index);
|
||||
virtual void Clear() { }
|
||||
virtual void AddXform();
|
||||
virtual void DuplicateXform();
|
||||
virtual void ClearCurrentXform();
|
||||
virtual void DeleteCurrentXform();
|
||||
virtual void AddFinalXform();
|
||||
virtual bool UseFinalXform() { return m_Ember.UseFinalXform(); }
|
||||
//virtual bool IsFinal(unsigned int i) { return false; }
|
||||
virtual size_t XformCount() { return m_Ember.XformCount(); }
|
||||
virtual size_t TotalXformCount() { return m_Ember.TotalXformCount(); }
|
||||
virtual string Name() { return m_Ember.m_Name; }
|
||||
virtual void Name(string s) { m_Ember.m_Name = s; }
|
||||
virtual unsigned int FinalRasW() { return m_Ember.m_FinalRasW; }
|
||||
virtual void FinalRasW(unsigned int w) { m_Ember.m_FinalRasW = w; }
|
||||
virtual unsigned int FinalRasH() { return m_Ember.m_FinalRasH; }
|
||||
virtual void FinalRasH(unsigned int h) { m_Ember.m_FinalRasH = h; }
|
||||
virtual void AddSymmetry(int sym, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) { m_Ember.AddSymmetry(sym, rand); }
|
||||
virtual void CalcNormalizedWeights() { m_Ember.CalcNormalizedWeights(m_NormalizedWeights); }
|
||||
Ember<T>* CurrentEmber();
|
||||
|
||||
//Menu.
|
||||
virtual void NewFlock(unsigned int count);
|
||||
virtual void NewEmptyFlameInCurrentFile();
|
||||
virtual void NewRandomFlameInCurrentFile();
|
||||
virtual void CopyFlameInCurrentFile();
|
||||
virtual void OpenAndPrepFiles(QStringList filenames, bool append);
|
||||
virtual void SaveCurrentAsXml();
|
||||
virtual void SaveEntireFileAsXml();
|
||||
virtual void SaveCurrentToOpenedFile();
|
||||
virtual void Undo();
|
||||
virtual void Redo();
|
||||
virtual void CopyXml();
|
||||
virtual void CopyAllXml();
|
||||
virtual void PasteXmlAppend();
|
||||
virtual void PasteXmlOver();
|
||||
virtual void AddReflectiveSymmetry();
|
||||
virtual void AddRotationalSymmetry();
|
||||
virtual void AddBothSymmetry();
|
||||
virtual void ClearFlame();
|
||||
|
||||
//Toolbar.
|
||||
|
||||
//Params.
|
||||
virtual void SetCenter(double x, double y);
|
||||
virtual void FillParamTablesAndPalette();
|
||||
virtual void BrightnessChanged(double d);
|
||||
virtual void GammaChanged(double d);
|
||||
virtual void GammaThresholdChanged(double d);
|
||||
virtual void VibrancyChanged(double d);
|
||||
virtual void HighlightPowerChanged(double d);
|
||||
virtual void PaletteModeChanged(unsigned int i);
|
||||
virtual void CenterXChanged(double d);
|
||||
virtual void CenterYChanged(double d);
|
||||
virtual void ScaleChanged(double d);
|
||||
virtual void ZoomChanged(double d);
|
||||
virtual void RotateChanged(double d);
|
||||
virtual void ZPosChanged(double d);
|
||||
virtual void PerspectiveChanged(double d);
|
||||
virtual void PitchChanged(double d);
|
||||
virtual void YawChanged(double d);
|
||||
virtual void DepthBlurChanged(double d);
|
||||
virtual void SpatialFilterWidthChanged(double d);
|
||||
virtual void SpatialFilterTypeChanged(const QString& text);
|
||||
virtual void TemporalFilterWidthChanged(double d);
|
||||
virtual void TemporalFilterTypeChanged(const QString& text);
|
||||
virtual void DEFilterMinRadiusWidthChanged(double d);
|
||||
virtual void DEFilterMaxRadiusWidthChanged(double d);
|
||||
virtual void DEFilterCurveWidthChanged(double d);
|
||||
virtual void PassesChanged(int d);
|
||||
virtual void TemporalSamplesChanged(int d);
|
||||
virtual void QualityChanged(double d);
|
||||
virtual void SupersampleChanged(int d);
|
||||
virtual void AffineInterpTypeChanged(int index);
|
||||
virtual void InterpTypeChanged(int index);
|
||||
virtual void BackgroundChanged(const QColor& col);
|
||||
|
||||
//Xforms.
|
||||
virtual void CurrentXformComboChanged(int index);
|
||||
virtual void XformWeightChanged(double d);
|
||||
virtual void EqualizeWeights();
|
||||
virtual void XformNameChanged(int row, int col);
|
||||
void FillWithXform(Xform<T>* xform);
|
||||
Xform<T>* CurrentXform();
|
||||
|
||||
//Xforms Affine.
|
||||
virtual void AffineSetHelper(double d, int index, bool pre);
|
||||
virtual void FlipCurrentXform(bool horizontal, bool vertical, bool pre);
|
||||
virtual void RotateCurrentXformByAngle(double angle, bool pre);
|
||||
virtual void MoveCurrentXform(double x, double y, bool pre);
|
||||
virtual void ScaleCurrentXform(double scale, bool pre);
|
||||
virtual void ResetCurrentXformAffine(bool pre);
|
||||
void FillAffineWithXform(Xform<T>* xform, bool pre);
|
||||
|
||||
//Xforms Color.
|
||||
virtual void XformColorIndexChanged(double d, bool updateRender);
|
||||
virtual void XformScrollColorIndexChanged(int d);
|
||||
virtual void XformColorSpeedChanged(double d);
|
||||
virtual void XformOpacityChanged(double d);
|
||||
virtual void XformDirectColorChanged(double d);
|
||||
void FillColorWithXform(Xform<T>* xform);
|
||||
|
||||
//Xforms Variations.
|
||||
virtual void SetupVariationTree();
|
||||
virtual void ClearVariationsTree();
|
||||
virtual void VariationSpinBoxValueChanged(double d);
|
||||
void FillVariationTreeWithXform(Xform<T>* xform);
|
||||
|
||||
//Xforms Xaos.
|
||||
virtual void FillXaosWithCurrentXform();
|
||||
virtual QString MakeXaosNameString(unsigned int i);
|
||||
virtual void XaosChanged(DoubleSpinBox* sender);
|
||||
virtual void ClearXaos();
|
||||
|
||||
//Palette.
|
||||
virtual bool InitPaletteTable(string s);
|
||||
virtual void ApplyPaletteToEmber();
|
||||
virtual void PaletteAdjust();
|
||||
virtual QRgb GetQRgbFromPaletteIndex(unsigned int 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(unsigned int start = UINT_MAX, unsigned int end = UINT_MAX);
|
||||
virtual void StopPreviewRender();
|
||||
|
||||
//Info.
|
||||
|
||||
//Rendering/progress.
|
||||
virtual bool Render();
|
||||
virtual bool CreateRenderer(eRendererType renderType, unsigned int platform, unsigned int device, bool shared = true);
|
||||
virtual unsigned int SizeOfT() { return sizeof(T); }
|
||||
virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs);
|
||||
virtual void ClearUndo();
|
||||
virtual GLEmberControllerBase* GLController() { return m_GLController.get(); }
|
||||
|
||||
private:
|
||||
//Embers.
|
||||
void ApplyXmlSavingTemplate(Ember<T>& ember);
|
||||
template <typename U> void SetEmberPrivate(const Ember<U>& ember, bool verbatim);
|
||||
|
||||
//Params.
|
||||
void ParamsToEmber(Ember<T>& ember);
|
||||
|
||||
//Xforms.
|
||||
void SetNormalizedWeightText(Xform<T>* xform);
|
||||
bool IsFinal(Xform<T>* xform);
|
||||
|
||||
//Xforms Color.
|
||||
void SetCurrentXformColorIndex(double d);
|
||||
|
||||
//Palette.
|
||||
void UpdateAdjustedPaletteGUI(Palette<T>& palette);
|
||||
|
||||
//Rendering/progress.
|
||||
void Update(std::function<void (void)> func, bool updateRender = true, eProcessAction action = FULL_RENDER);
|
||||
void UpdateCurrentXform(std::function<void (Xform<T>*)> func, bool updateRender = true, eProcessAction action = FULL_RENDER);
|
||||
|
||||
//Templated members.
|
||||
bool m_PreviewRun;
|
||||
bool m_PreviewRunning;
|
||||
vector<T> m_TempOpacities;
|
||||
vector<T> m_NormalizedWeights;
|
||||
Ember<T> m_Ember;
|
||||
EmberFile<T> m_EmberFile;
|
||||
deque<Ember<T>> m_UndoList;
|
||||
Palette<T> m_TempPalette;
|
||||
PaletteList<T> m_PaletteList;
|
||||
VariationList<T> m_VariationList;
|
||||
auto_ptr<SheepTools<T, T>> m_SheepTools;
|
||||
auto_ptr<GLEmberController<T>> m_GLController;
|
||||
auto_ptr<EmberNs::Renderer<T, T>> m_PreviewRenderer;
|
||||
QFuture<void> m_PreviewResult;
|
||||
std::function<void (unsigned int, unsigned int)> m_PreviewRenderFunc;
|
||||
};
|
||||
|
||||
template class FractoriumEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class FractoriumEmberController<double>;
|
||||
#endif
|
58
Source/Fractorium/FractoriumInfo.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Update the histogram bounds display labels.
|
||||
/// This shows the user the actual bounds of what's
|
||||
/// being rendered. Mostly of engineering interest.
|
||||
/// </summary>
|
||||
void Fractorium::UpdateHistogramBounds()
|
||||
{
|
||||
if (RendererBase* r = m_Controller->Renderer())
|
||||
{
|
||||
sprintf_s(m_ULString, 32, "UL: %3.3f, %3.3f", r->LowerLeftX(), r->UpperRightY());//These bounds include gutter padding.
|
||||
sprintf_s(m_URString, 32, "UR: %3.3f, %3.3f", -r->LowerLeftX(), r->UpperRightY());
|
||||
sprintf_s(m_LRString, 32, "LR: %3.3f, %3.3f", -r->LowerLeftX(), -r->UpperRightY());
|
||||
sprintf_s(m_LLString, 32, "LL: %3.3f, %3.3f", r->LowerLeftX(), -r->UpperRightY());
|
||||
sprintf_s(m_WString, 16, "W: %4d" , r->SuperRasW());
|
||||
sprintf_s(m_HString, 16, "H: %4d" , r->SuperRasH());
|
||||
|
||||
ui.InfoBoundsLabelUL->setText(QString(m_ULString));
|
||||
ui.InfoBoundsLabelUR->setText(QString(m_URString));
|
||||
ui.InfoBoundsLabelLR->setText(QString(m_LRString));
|
||||
ui.InfoBoundsLabelLL->setText(QString(m_LLString));
|
||||
ui.InfoBoundsLabelW->setText(QString(m_WString));
|
||||
ui.InfoBoundsLabelH->setText(QString(m_HString));
|
||||
|
||||
ui.InfoBoundsTable->item(0, 1)->setText(QString::number(r->GutterWidth()));
|
||||
|
||||
if (r->GetDensityFilter())
|
||||
{
|
||||
unsigned int deWidth = (r->GetDensityFilter()->FilterWidth() * 2) + 1;
|
||||
|
||||
sprintf_s(m_DEString, 16, "%d x %d", deWidth, deWidth);
|
||||
ui.InfoBoundsTable->item(1, 1)->setText(QString(m_DEString));
|
||||
}
|
||||
else
|
||||
ui.InfoBoundsTable->item(1, 1)->setText("N/A");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the passed in QTextEdit with the vector of strings.
|
||||
/// Optionally clear first.
|
||||
/// Serves as a convenience function because the error reports coming
|
||||
/// from Ember and EmberCL use vector<string>.
|
||||
/// Use invokeMethod() in case this is called from a thread.
|
||||
/// </summary>
|
||||
/// <param name="errors">The vector of error strings</param>
|
||||
/// <param name="textEdit">The QTextEdit to fill</param>
|
||||
/// <param name="clear">Clear if true, else don't.</param>
|
||||
void Fractorium::ErrorReportToQTextEdit(vector<string>& errors, QTextEdit* textEdit, bool clear)
|
||||
{
|
||||
if (clear)
|
||||
QMetaObject::invokeMethod(textEdit, "clear", Qt::QueuedConnection);
|
||||
|
||||
for (size_t i = 0; i < errors.size(); i++)
|
||||
QMetaObject::invokeMethod(textEdit, "append", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(errors[i]) + "\n"));
|
||||
}
|
277
Source/Fractorium/FractoriumLibrary.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the library tree UI.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot function to be called via QMetaObject::invokeMethod() to update preview images in the preview thread.
|
||||
/// </summary>
|
||||
/// <param name="item">The item double clicked on</param>
|
||||
/// <param name="v">The vector holding the RGBA bitmap</param>
|
||||
/// <param name="width">The width of the bitmap</param>
|
||||
/// <param name="height">The height of the bitmap</param>
|
||||
void Fractorium::SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector<unsigned char>& v, unsigned int width, unsigned int height)
|
||||
{
|
||||
item->SetImage(v, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set all libary tree entries to the name of the corresponding ember they represent.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SyncNames()
|
||||
{
|
||||
EmberTreeWidgetItem<T>* item;
|
||||
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
||||
QTreeWidgetItem* top = tree->topLevelItem(0);
|
||||
|
||||
tree->blockSignals(true);
|
||||
|
||||
if (top)
|
||||
{
|
||||
for (int i = 0; i < top->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.m_Embers.size())//Cast the child widget to the EmberTreeWidgetItem type.
|
||||
item->setText(0, QString::fromStdString(m_EmberFile.m_Embers[i].m_Name));
|
||||
}
|
||||
}
|
||||
|
||||
tree->blockSignals(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the library tree with the names of the embers in the
|
||||
/// currently opened file.
|
||||
/// Start preview render thread.
|
||||
/// </summary>
|
||||
/// <param name="selectIndex">After the tree is filled, select this index. Pass -1 to omit selecting an index.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FillLibraryTree(int selectIndex)
|
||||
{
|
||||
unsigned int i, j, size = 64;
|
||||
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
||||
vector<unsigned char> v(size * size * 4);
|
||||
|
||||
StopPreviewRender();
|
||||
tree->clear();
|
||||
QCoreApplication::flush();
|
||||
|
||||
tree->blockSignals(true);
|
||||
|
||||
QTreeWidgetItem* fileItem = new QTreeWidgetItem(tree);
|
||||
QFileInfo info(m_EmberFile.m_Filename);
|
||||
|
||||
fileItem->setText(0, info.fileName());
|
||||
fileItem->setToolTip(0, m_EmberFile.m_Filename);
|
||||
fileItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
||||
|
||||
for (j = 0; j < m_EmberFile.m_Embers.size(); j++)
|
||||
{
|
||||
Ember<T>* ember = &m_EmberFile.m_Embers[j];
|
||||
EmberTreeWidgetItem<T>* emberItem = new EmberTreeWidgetItem<T>(ember, fileItem);
|
||||
|
||||
emberItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
||||
|
||||
if (ember->m_Name.empty())
|
||||
emberItem->setText(0, QString::number(j));
|
||||
else
|
||||
emberItem->setText(0, ember->m_Name.c_str());
|
||||
|
||||
emberItem->setToolTip(0, emberItem->text(0));
|
||||
emberItem->SetImage(v, size, size);
|
||||
}
|
||||
|
||||
tree->blockSignals(false);
|
||||
|
||||
if (selectIndex != -1)
|
||||
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
||||
if (EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(selectIndex)))
|
||||
emberItem->setSelected(true);
|
||||
|
||||
QCoreApplication::flush();
|
||||
RenderPreviews(0, m_EmberFile.m_Embers.size());
|
||||
tree->expandAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the library tree with the newly added embers (most likely from pasting) and
|
||||
/// only render previews for the new ones, without clearing the entire tree.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::UpdateLibraryTree()
|
||||
{
|
||||
unsigned int i, size = 64;
|
||||
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
||||
vector<unsigned char> v(size * size * 4);
|
||||
|
||||
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
||||
{
|
||||
int childCount = top->childCount();
|
||||
|
||||
tree->blockSignals(true);
|
||||
|
||||
for (i = childCount; i < m_EmberFile.m_Embers.size(); i++)
|
||||
{
|
||||
Ember<T>* ember = &m_EmberFile.m_Embers[i];
|
||||
EmberTreeWidgetItem<T>* emberItem = new EmberTreeWidgetItem<T>(ember, top);
|
||||
|
||||
emberItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
||||
|
||||
if (ember->m_Name.empty())
|
||||
emberItem->setText(0, QString::number(i));
|
||||
else
|
||||
emberItem->setText(0, ember->m_Name.c_str());
|
||||
|
||||
emberItem->setToolTip(0, emberItem->text(0));
|
||||
emberItem->SetImage(v, size, size);
|
||||
}
|
||||
|
||||
//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.m_Embers.size(); i++)
|
||||
{
|
||||
if (EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
|
||||
emberItem->SetEmberPointer(&m_EmberFile.m_Embers[i]);
|
||||
}
|
||||
|
||||
tree->blockSignals(false);
|
||||
RenderPreviews(childCount, m_EmberFile.m_Embers.size());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the text of the item which was changed to the name of the current ember.
|
||||
/// Ensure all names are unique in the opened file.
|
||||
/// This seems to be called spuriously, so we do a check inside to make sure
|
||||
/// the text was actually changed.
|
||||
/// We also have to wrap the dynamic_cast call in a try/catch block because this can
|
||||
/// be called on a widget that has already been deleted.
|
||||
/// </summary>
|
||||
/// <param name="item">The libary tree item changed</param>
|
||||
/// <param name="col">The column clicked, ignored.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::EmberTreeItemChanged(QTreeWidgetItem* item, int col)
|
||||
{
|
||||
try
|
||||
{
|
||||
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
||||
EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(item);
|
||||
|
||||
if (emberItem)
|
||||
{
|
||||
string oldName = emberItem->GetEmber()->m_Name;//First preserve the previous name.
|
||||
|
||||
tree->blockSignals(true);
|
||||
emberItem->UpdateEmberName();//Copy edit text to the ember's name variable.
|
||||
m_EmberFile.MakeNamesUnique();//Ensure all names remain unique.
|
||||
SyncNames();//Copy all ember names to the tree items since some might have changed to be made unique.
|
||||
string newName = emberItem->GetEmber()->m_Name;//Get the new, final, unique name.
|
||||
|
||||
if (m_Ember.m_Name == oldName && oldName != newName)//If the ember edited was the current one, and the name was indeed changed, update the name of the current one.
|
||||
{
|
||||
m_Ember.m_Name = newName;
|
||||
m_LastSaveCurrent = "";//Reset will force the dialog to show on the next save current since the user probably wants a different name.
|
||||
}
|
||||
|
||||
tree->blockSignals(false);
|
||||
}
|
||||
else if (QTreeWidgetItem* parentItem = dynamic_cast<QTreeWidgetItem*>(item))
|
||||
{
|
||||
QString text = parentItem->text(0);
|
||||
|
||||
if (text != "")
|
||||
{
|
||||
m_EmberFile.m_Filename = text;
|
||||
m_LastSaveAll = "";//Reset will force the dialog to show on the next save all since the user probably wants a different name.
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
qDebug() << "FractoriumEmberController<T>::EmberTreeItemChanged() : Exception thrown: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnEmberTreeItemChanged(QTreeWidgetItem* item, int col) { m_Controller->EmberTreeItemChanged(item, col); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the current ember to the selected item.
|
||||
/// Clears the undo state.
|
||||
/// Resets the rendering process.
|
||||
/// Called when the user double clicks on a library tree item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item double clicked on</param>
|
||||
/// <param name="col">The column clicked, ignored.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col)
|
||||
{
|
||||
if (EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(item))
|
||||
{
|
||||
ClearUndo();
|
||||
SetEmber(*emberItem->GetEmber());
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { m_Controller->EmberTreeItemDoubleClicked(item, col); }
|
||||
|
||||
/// <summary>
|
||||
/// Stop the preview renderer if it's already running.
|
||||
/// Clear all of the existing preview images, then start the preview rendering thread.
|
||||
/// Optionally only render previews for a subset of all open embers.
|
||||
/// </summary>
|
||||
/// <param name="start">The 0-based index to start rendering previews for</param>
|
||||
/// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::RenderPreviews(unsigned int start, unsigned int end)
|
||||
{
|
||||
StopPreviewRender();
|
||||
|
||||
if (start == UINT_MAX && end == UINT_MAX)
|
||||
{
|
||||
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
||||
|
||||
tree->blockSignals(true);
|
||||
|
||||
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
||||
{
|
||||
int childCount = top->childCount();
|
||||
vector<unsigned char> emptyPreview(PREVIEW_SIZE * PREVIEW_SIZE * 3);
|
||||
|
||||
for (int i = 0; i < childCount; i++)
|
||||
if (EmberTreeWidgetItem<T>* treeItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
|
||||
treeItem->SetImage(emptyPreview, PREVIEW_SIZE, PREVIEW_SIZE);
|
||||
}
|
||||
|
||||
tree->blockSignals(false);
|
||||
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, 0, m_EmberFile.m_Embers.size());
|
||||
}
|
||||
else
|
||||
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, start, end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the preview rendering thread.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::StopPreviewRender()
|
||||
{
|
||||
m_PreviewRun = false;
|
||||
|
||||
while (m_PreviewRunning)
|
||||
QApplication::processEvents();
|
||||
|
||||
m_PreviewResult.cancel();
|
||||
|
||||
while (m_PreviewResult.isRunning())
|
||||
QApplication::processEvents();
|
||||
|
||||
QCoreApplication::sendPostedEvents(m_Fractorium->ui.LibraryTree);
|
||||
QCoreApplication::flush();
|
||||
}
|
752
Source/Fractorium/FractoriumMenus.cpp
Normal file
@ -0,0 +1,752 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the menus UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitMenusUI()
|
||||
{
|
||||
//File menu.
|
||||
connect(ui.ActionNewFlock, SIGNAL(triggered(bool)), this, SLOT(OnActionNewFlock(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionNewEmptyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewEmptyFlameInCurrentFile(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionNewRandomFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewRandomFlameInCurrentFile(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionCopyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyFlameInCurrentFile(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionOpen, SIGNAL(triggered(bool)), this, SLOT(OnActionOpen(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionSaveCurrentAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentAsXml(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionSaveEntireFileAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveEntireFileAsXml(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionSaveCurrentToOpenedFile, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentToOpenedFile(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionSaveCurrentScreen, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentScreen(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionExit, SIGNAL(triggered(bool)), this, SLOT(OnActionExit(bool)), Qt::QueuedConnection);
|
||||
|
||||
//Edit menu.
|
||||
connect(ui.ActionUndo, SIGNAL(triggered(bool)), this, SLOT(OnActionUndo(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionRedo, SIGNAL(triggered(bool)), this, SLOT(OnActionRedo(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionCopyXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyXml(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionCopyAllXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyAllXml(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionPasteXmlAppend, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlAppend(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionPasteXmlOver, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlOver(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);
|
||||
connect(ui.ActionAddBothSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddBothSymmetry(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionClearFlame, SIGNAL(triggered(bool)), this, SLOT(OnActionClearFlame(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionStopRenderingPreviews, SIGNAL(triggered(bool)), this, SLOT(OnActionStopRenderingPreviews(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionRenderPreviews, SIGNAL(triggered(bool)), this, SLOT(OnActionRenderPreviews(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ActionFinalRender, SIGNAL(triggered(bool)), this, SLOT(OnActionFinalRender(bool)), Qt::QueuedConnection);
|
||||
connect(m_FinalRenderDialog, SIGNAL(finished(int)), this, SLOT(OnFinalRenderClose(int)), Qt::QueuedConnection);
|
||||
connect(ui.ActionOptions, SIGNAL(triggered(bool)), this, SLOT(OnActionOptions(bool)), Qt::QueuedConnection);
|
||||
|
||||
//Help menu.
|
||||
connect(ui.ActionAbout, SIGNAL(triggered(bool)), this, SLOT(OnActionAbout(bool)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new flock of random embers, with the specified length.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of embers to include in the flock</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::NewFlock(unsigned int count)
|
||||
{
|
||||
Ember<T> ember;
|
||||
|
||||
StopPreviewRender();
|
||||
m_EmberFile.Clear();
|
||||
m_EmberFile.m_Embers.reserve(count);
|
||||
m_EmberFile.m_Filename = EmberFile<T>::DefaultFilename();
|
||||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
m_SheepTools->Random(ember);
|
||||
ParamsToEmber(ember);
|
||||
ember.m_OrigFinalRasW = ember.m_FinalRasW;
|
||||
ember.m_OrigFinalRasH = ember.m_FinalRasH;
|
||||
ember.m_Name = m_EmberFile.m_Filename.toStdString() + "-" + QString::number(i + 1).toStdString();
|
||||
m_EmberFile.m_Embers.push_back(ember);
|
||||
}
|
||||
|
||||
m_LastSaveAll = "";
|
||||
FillLibraryTree();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new flock and assign the first ember as the current one.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionNewFlock(bool checked)
|
||||
{
|
||||
m_Controller->NewFlock(10);
|
||||
m_Controller->SetEmber(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and add a new empty ember in the currently opened file
|
||||
/// and set it as the current one.
|
||||
/// It will have one empty xform in it.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::NewEmptyFlameInCurrentFile()
|
||||
{
|
||||
Ember<T> ember;
|
||||
Xform<T> xform;
|
||||
QDateTime local(QDateTime::currentDateTime());
|
||||
|
||||
StopPreviewRender();
|
||||
ParamsToEmber(ember);
|
||||
ember.m_OrigFinalRasW = ember.m_FinalRasW;
|
||||
ember.m_OrigFinalRasH = ember.m_FinalRasH;
|
||||
xform.m_Weight = T(0.25);
|
||||
xform.m_ColorX = m_Rand.Frand01<T>();
|
||||
ember.AddXform(xform);
|
||||
ember.m_Palette = *m_PaletteList.GetPalette(-1);
|
||||
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.m_Embers.size() + 1).toStdString();
|
||||
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
||||
m_EmberFile.MakeNamesUnique();
|
||||
UpdateLibraryTree();
|
||||
SetEmber(m_EmberFile.m_Embers.size() - 1);
|
||||
}
|
||||
|
||||
void Fractorium::OnActionNewEmptyFlameInCurrentFile(bool checked) { m_Controller->NewEmptyFlameInCurrentFile(); }
|
||||
|
||||
/// <summary>
|
||||
/// Create and add a new random ember in the currently opened file
|
||||
/// and set it as the current one.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::NewRandomFlameInCurrentFile()
|
||||
{
|
||||
Ember<T> ember;
|
||||
|
||||
StopPreviewRender();
|
||||
m_SheepTools->Random(ember);
|
||||
ParamsToEmber(ember);
|
||||
ember.m_OrigFinalRasW = ember.m_FinalRasW;
|
||||
ember.m_OrigFinalRasH = ember.m_FinalRasH;
|
||||
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.m_Embers.size() + 1).toStdString();
|
||||
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
||||
m_EmberFile.MakeNamesUnique();
|
||||
UpdateLibraryTree();
|
||||
SetEmber(m_EmberFile.m_Embers.size() - 1);
|
||||
}
|
||||
|
||||
void Fractorium::OnActionNewRandomFlameInCurrentFile(bool checked) { m_Controller->NewRandomFlameInCurrentFile(); }
|
||||
|
||||
/// <summary>
|
||||
/// Create and add a a copy of the current ember in the currently opened file
|
||||
/// and set it as the current one.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::CopyFlameInCurrentFile()
|
||||
{
|
||||
Ember<T> ember = m_Ember;
|
||||
|
||||
StopPreviewRender();
|
||||
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.m_Embers.size() + 1).toStdString();
|
||||
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
||||
m_EmberFile.MakeNamesUnique();
|
||||
UpdateLibraryTree();
|
||||
SetEmber(m_EmberFile.m_Embers.size() - 1);
|
||||
}
|
||||
|
||||
void Fractorium::OnActionCopyFlameInCurrentFile(bool checked) { m_Controller->CopyFlameInCurrentFile(); }
|
||||
|
||||
/// <summary>
|
||||
/// Open a list of ember Xml files, apply various values from the GUI widgets.
|
||||
/// Either append these newly read embers to the existing open embers,
|
||||
/// or clear the current ember file first.
|
||||
/// When appending, add the new embers the the end of library tree.
|
||||
/// When not appending, clear and populate the library tree with the new embers.
|
||||
/// Set the current ember to the first one in the newly opened list.
|
||||
/// Clears the undo state.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="filenames">A list of full paths and filenames</param>
|
||||
/// <param name="append">True to append the embers in the new files to the end of the currently open embers, false to clear and replace them</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::OpenAndPrepFiles(QStringList filenames, bool append)
|
||||
{
|
||||
if (!filenames.empty())
|
||||
{
|
||||
size_t i;
|
||||
EmberFile<T> emberFile;
|
||||
XmlToEmber<T> parser;
|
||||
vector<Ember<T>> embers;
|
||||
unsigned int previousSize = append ? m_EmberFile.m_Embers.size() : 0;
|
||||
|
||||
StopPreviewRender();
|
||||
emberFile.m_Filename = filenames[0];
|
||||
|
||||
foreach (QString filename, filenames)
|
||||
{
|
||||
embers.clear();
|
||||
|
||||
if (parser.Parse(filename.toStdString().c_str(), embers) && !embers.empty())
|
||||
{
|
||||
//Disregard whatever size was specified in the file and fit it to the output window size.
|
||||
for (i = 0; i < embers.size(); i++)
|
||||
{
|
||||
embers[i].SetSizeAndAdjustScale(m_Fractorium->ui.GLDisplay->width(), m_Fractorium->ui.GLDisplay->height(), true, SCALE_WIDTH);
|
||||
|
||||
//Also ensure it has a name.
|
||||
if (embers[i].m_Name == "" || embers[i].m_Name == "No name")
|
||||
embers[i].m_Name = QString::number(i).toStdString();
|
||||
|
||||
embers[i].m_Quality = m_Fractorium->m_QualitySpin->value();
|
||||
embers[i].m_Supersample = m_Fractorium->m_SupersampleSpin->value();
|
||||
}
|
||||
|
||||
m_LastSaveAll = "";
|
||||
emberFile.m_Embers.insert(emberFile.m_Embers.end(), embers.begin(), embers.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<string> errors = parser.ErrorReport();
|
||||
|
||||
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
|
||||
QMessageBox::critical(m_Fractorium, "Open Failed", "Could not open file, see info tab for details.");
|
||||
}
|
||||
}
|
||||
|
||||
if (append)
|
||||
{
|
||||
if (m_EmberFile.m_Filename == "")
|
||||
m_EmberFile.m_Filename = filenames[0];
|
||||
|
||||
m_EmberFile.m_Embers.insert(m_EmberFile.m_Embers.end(), emberFile.m_Embers.begin(), emberFile.m_Embers.end());
|
||||
}
|
||||
else
|
||||
m_EmberFile = emberFile;
|
||||
|
||||
//Resync indices and names.
|
||||
for (i = 0; i < m_EmberFile.m_Embers.size(); i++)
|
||||
m_EmberFile.m_Embers[i].m_Index = i;
|
||||
|
||||
m_EmberFile.MakeNamesUnique();
|
||||
|
||||
if (append)
|
||||
UpdateLibraryTree();
|
||||
else
|
||||
FillLibraryTree(append ? previousSize - 1 : 0);
|
||||
|
||||
ClearUndo();
|
||||
SetEmber(previousSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show a file open dialog to open ember Xml files.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionOpen(bool checked) { m_Controller->OpenAndPrepFiles(SetupOpenXmlDialog(), false); }
|
||||
|
||||
/// <summary>
|
||||
/// Save current ember as Xml, using the Xml saving template values from the options.
|
||||
/// This will first save the current ember back to the opened file in memory before
|
||||
/// saving it to disk.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SaveCurrentAsXml()
|
||||
{
|
||||
QString filename;
|
||||
FractoriumSettings* s = m_Fractorium->m_Settings;
|
||||
|
||||
if (QFile::exists(m_LastSaveCurrent))
|
||||
{
|
||||
filename = m_LastSaveCurrent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_EmberFile.m_Embers.size() == 1)
|
||||
filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename);//If only one ember present, just use parent filename.
|
||||
else
|
||||
filename = m_Fractorium->SetupSaveXmlDialog(QString::fromStdString(m_Ember.m_Name));//More than one ember present, use individual ember name.
|
||||
}
|
||||
|
||||
if (filename != "")
|
||||
{
|
||||
Ember<T> ember = m_Ember;
|
||||
EmberToXml<T> writer;
|
||||
QFileInfo fileInfo(filename);
|
||||
xmlDocPtr tempEdit = ember.m_Edits;
|
||||
|
||||
SaveCurrentToOpenedFile();//Save the current ember back to the opened file before writing to disk.
|
||||
ApplyXmlSavingTemplate(ember);
|
||||
ember.m_Edits = writer.CreateNewEditdoc(&ember, NULL, "edit", s->Nick().toStdString(), s->Url().toStdString(), s->Id().toStdString(), "", 0, 0);
|
||||
|
||||
if (tempEdit != NULL)
|
||||
xmlFreeDoc(tempEdit);
|
||||
|
||||
if (writer.Save(filename.toStdString().c_str(), ember, 0, true, false, true))
|
||||
{
|
||||
s->SaveFolder(fileInfo.canonicalPath());
|
||||
m_LastSaveCurrent = filename;
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(m_Fractorium, "Save Failed", "Could not save file, try saving to a different folder.");
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnActionSaveCurrentAsXml(bool checked) { m_Controller->SaveCurrentAsXml(); }
|
||||
|
||||
/// <summary>
|
||||
/// Save entire opened file Xml, using the Xml saving template values from the options on each ember.
|
||||
/// This will first save the current ember back to the opened file in memory before
|
||||
/// saving all to disk.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SaveEntireFileAsXml()
|
||||
{
|
||||
QString filename;
|
||||
FractoriumSettings* s = m_Fractorium->m_Settings;
|
||||
|
||||
if (QFile::exists(m_LastSaveAll))
|
||||
filename = m_LastSaveAll;
|
||||
else
|
||||
filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename);
|
||||
|
||||
if (filename != "")
|
||||
{
|
||||
EmberFile<T> emberFile;
|
||||
EmberToXml<T> writer;
|
||||
QFileInfo fileInfo(filename);
|
||||
|
||||
SaveCurrentToOpenedFile();//Save the current ember back to the opened file before writing to disk.
|
||||
emberFile = m_EmberFile;
|
||||
|
||||
for (size_t i = 0; i < emberFile.m_Embers.size(); i++)
|
||||
ApplyXmlSavingTemplate(emberFile.m_Embers[i]);
|
||||
|
||||
if (writer.Save(filename.toStdString().c_str(), emberFile.m_Embers, 0, true, false, true))
|
||||
{
|
||||
m_LastSaveAll = filename;
|
||||
s->SaveFolder(fileInfo.canonicalPath());
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(m_Fractorium, "Save Failed", "Could not save file, try saving to a different folder.");
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnActionSaveEntireFileAsXml(bool checked) { m_Controller->SaveEntireFileAsXml(); }
|
||||
|
||||
/// <summary>
|
||||
/// Show a file save dialog and save what is currently shown in the render window to disk as an image.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionSaveCurrentScreen(bool checked)
|
||||
{
|
||||
QString filename = SetupSaveImageDialog(QString::fromStdString(m_Controller->Name()));
|
||||
|
||||
m_Controller->SaveCurrentRender(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current ember back to its position in the opened file.
|
||||
/// This does not save to disk.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SaveCurrentToOpenedFile()
|
||||
{
|
||||
size_t i;
|
||||
bool fileFound = false;
|
||||
|
||||
for (i = 0; i < m_EmberFile.m_Embers.size(); i++)
|
||||
{
|
||||
if (m_Ember.m_Name == m_EmberFile.m_Embers[i].m_Name)
|
||||
{
|
||||
m_EmberFile.m_Embers[i] = m_Ember;
|
||||
fileFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileFound)
|
||||
{
|
||||
StopPreviewRender();
|
||||
m_EmberFile.m_Embers.push_back(m_Ember);
|
||||
m_EmberFile.MakeNamesUnique();
|
||||
UpdateLibraryTree();
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderPreviews(i, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnActionSaveCurrentToOpenedFile(bool checked) { m_Controller->SaveCurrentToOpenedFile(); }
|
||||
|
||||
/// <summary>
|
||||
/// Exit the application.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignore.</param>
|
||||
void Fractorium::OnActionExit(bool checked)
|
||||
{
|
||||
closeEvent(NULL);
|
||||
QApplication::exit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Undoes this instance.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::Undo()
|
||||
{
|
||||
if (m_UndoList.size() > 1 && m_UndoIndex > 0)
|
||||
{
|
||||
int index = m_Ember.GetTotalXformIndex(CurrentXform());
|
||||
|
||||
m_LastEditWasUndoRedo = true;
|
||||
m_UndoIndex = max(0u, m_UndoIndex - 1u);
|
||||
SetEmber(m_UndoList[m_UndoIndex], true);
|
||||
m_EditState = UNDO_REDO;
|
||||
|
||||
if (index >= 0)
|
||||
m_Fractorium->CurrentXform(index);
|
||||
|
||||
m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
|
||||
m_Fractorium->ui.ActionRedo->setEnabled(m_UndoList.size() > 1 && !(m_UndoIndex == m_UndoList.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnActionUndo(bool checked) { m_Controller->Undo(); }
|
||||
|
||||
/// <summary>
|
||||
/// Redoes this instance.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::Redo()
|
||||
{
|
||||
if (m_UndoList.size() > 1 && m_UndoIndex < m_UndoList.size() - 1)
|
||||
{
|
||||
int index = m_Ember.GetTotalXformIndex(CurrentXform());
|
||||
|
||||
m_LastEditWasUndoRedo = true;
|
||||
m_UndoIndex = min<unsigned int>(m_UndoIndex + 1, m_UndoList.size() - 1);
|
||||
SetEmber(m_UndoList[m_UndoIndex], true);
|
||||
m_EditState = UNDO_REDO;
|
||||
|
||||
if (index >= 0)
|
||||
m_Fractorium->CurrentXform(index);
|
||||
|
||||
m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
|
||||
m_Fractorium->ui.ActionRedo->setEnabled(m_UndoList.size() > 1 && !(m_UndoIndex == m_UndoList.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnActionRedo(bool checked) { m_Controller->Redo(); }
|
||||
|
||||
/// <summary>
|
||||
/// Copy the current ember Xml to the clipboard.
|
||||
/// Apply Xml saving settings from the options first.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::CopyXml()
|
||||
{
|
||||
Ember<T> ember = m_Ember;
|
||||
EmberToXml<T> emberToXml;
|
||||
FractoriumSettings* settings = m_Fractorium->m_Settings;
|
||||
|
||||
ember.m_FinalRasW = settings->XmlWidth();
|
||||
ember.m_FinalRasH = settings->XmlHeight();
|
||||
ember.m_Quality = settings->XmlQuality();
|
||||
ember.m_Supersample = settings->XmlSupersample();
|
||||
ember.m_TemporalSamples = settings->XmlTemporalSamples();
|
||||
QApplication::clipboard()->setText(QString::fromStdString(emberToXml.ToString(ember, "", 0, false, false, true)));
|
||||
}
|
||||
|
||||
void Fractorium::OnActionCopyXml(bool checked) { m_Controller->CopyXml(); }
|
||||
|
||||
/// <summary>
|
||||
/// Copy the Xmls for all open embers as a single string to the clipboard, enclosed with the <flames> tag.
|
||||
/// Apply Xml saving settings from the options first for each ember.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::CopyAllXml()
|
||||
{
|
||||
ostringstream os;
|
||||
EmberToXml<T> emberToXml;
|
||||
FractoriumSettings* settings = m_Fractorium->m_Settings;
|
||||
|
||||
os << "<flames>\n";
|
||||
|
||||
for (size_t i = 0; i < m_EmberFile.m_Embers.size(); i++)
|
||||
{
|
||||
Ember<T> ember = m_EmberFile.m_Embers[i];
|
||||
|
||||
ember.m_FinalRasW = settings->XmlWidth();
|
||||
ember.m_FinalRasH = settings->XmlHeight();
|
||||
ember.m_Quality = settings->XmlQuality();
|
||||
ember.m_Supersample = settings->XmlSupersample();
|
||||
ember.m_TemporalSamples = settings->XmlTemporalSamples();
|
||||
|
||||
os << emberToXml.ToString(ember, "", 0, false, false, true);
|
||||
}
|
||||
|
||||
os << "</flames>\n";
|
||||
QApplication::clipboard()->setText(QString::fromStdString(os.str()));
|
||||
}
|
||||
|
||||
void Fractorium::OnActionCopyAllXml(bool checked) { m_Controller->CopyAllXml(); }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the Xml text from the clipboard to an ember, add it to the end
|
||||
/// of the current file and set it as the current ember. If multiple Xmls were
|
||||
/// copied to the clipboard and were enclosed in <flames> tags, then all of them will be added.
|
||||
/// Clears the undo state.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::PasteXmlAppend()
|
||||
{
|
||||
unsigned int i, previousSize = m_EmberFile.m_Embers.size();
|
||||
string s, errors;
|
||||
XmlToEmber<T> parser;
|
||||
vector<Ember<T>> embers;
|
||||
QTextCodec* codec = QTextCodec::codecForName("UTF-8");
|
||||
QByteArray b = codec->fromUnicode(QApplication::clipboard()->text());
|
||||
|
||||
s.reserve(b.size());
|
||||
|
||||
for (i = 0; i < b.size(); i++)
|
||||
{
|
||||
if ((unsigned int)b[i] < 128u)
|
||||
s.push_back(b[i]);
|
||||
}
|
||||
|
||||
b.clear();
|
||||
StopPreviewRender();
|
||||
parser.Parse((unsigned char*)s.c_str(), "", embers);
|
||||
errors = parser.ErrorReportString();
|
||||
|
||||
if (errors != "")
|
||||
{
|
||||
QMessageBox::critical(m_Fractorium, "Paste Error", QString::fromStdString(errors));
|
||||
}
|
||||
|
||||
if (!embers.empty())
|
||||
{
|
||||
for (i = 0; i < embers.size(); i++)
|
||||
{
|
||||
//Disregard whatever size was specified in the file and fit it to the output window size.
|
||||
embers[i].m_Index = m_EmberFile.m_Embers.size();
|
||||
embers[i].SetSizeAndAdjustScale(m_Fractorium->ui.GLDisplay->width(), m_Fractorium->ui.GLDisplay->height(), true, SCALE_WIDTH);
|
||||
|
||||
//Also ensure it has a name.
|
||||
if (embers[i].m_Name == "" || embers[i].m_Name == "No name")
|
||||
embers[i].m_Name = QString::number(embers[i].m_Index).toStdString();
|
||||
|
||||
m_EmberFile.m_Embers.push_back(embers[i]);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
||||
}
|
||||
|
||||
m_EmberFile.MakeNamesUnique();
|
||||
UpdateLibraryTree();
|
||||
SetEmber(previousSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnActionPasteXmlAppend(bool checked) { m_Controller->PasteXmlAppend(); }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the Xml text from the clipboard to an ember, overwrite the
|
||||
/// current file and set the first as the current ember. If multiple Xmls were
|
||||
/// copied to the clipboard and were enclosed in <flames> tags, then the current file will contain all of them.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::PasteXmlOver()
|
||||
{
|
||||
unsigned int i;
|
||||
string s, errors;
|
||||
XmlToEmber<T> parser;
|
||||
Ember<T> backupEmber = m_EmberFile.m_Embers[0];
|
||||
QTextCodec* codec = QTextCodec::codecForName("UTF-8");
|
||||
QByteArray b = codec->fromUnicode(QApplication::clipboard()->text());
|
||||
|
||||
s.reserve(b.size());
|
||||
|
||||
for (i = 0; i < b.size(); i++)
|
||||
{
|
||||
if ((unsigned int)b[i] < 128u)
|
||||
s.push_back(b[i]);
|
||||
}
|
||||
|
||||
b.clear();
|
||||
StopPreviewRender();
|
||||
m_EmberFile.m_Embers.clear();//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
||||
parser.Parse((unsigned char*)s.c_str(), "", m_EmberFile.m_Embers);
|
||||
errors = parser.ErrorReportString();
|
||||
|
||||
if (errors != "")
|
||||
{
|
||||
QMessageBox::critical(m_Fractorium, "Paste Error", QString::fromStdString(errors));
|
||||
}
|
||||
|
||||
if (!m_EmberFile.m_Embers.empty())
|
||||
{
|
||||
for (i = 0; i < m_EmberFile.m_Embers.size(); i++)
|
||||
{
|
||||
//Disregard whatever size was specified in the file and fit it to the output window size.
|
||||
m_EmberFile.m_Embers[i].m_Index = i;
|
||||
m_EmberFile.m_Embers[i].SetSizeAndAdjustScale(m_Fractorium->ui.GLDisplay->width(), m_Fractorium->ui.GLDisplay->height(), true, SCALE_WIDTH);
|
||||
|
||||
//Also ensure it has a name.
|
||||
if (m_EmberFile.m_Embers[i].m_Name == "" || m_EmberFile.m_Embers[i].m_Name == "No name")
|
||||
m_EmberFile.m_Embers[i].m_Name = QString::number(m_EmberFile.m_Embers[i].m_Index).toStdString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
backupEmber.m_Index = 0;
|
||||
m_EmberFile.m_Embers.push_back(backupEmber);
|
||||
}
|
||||
|
||||
m_EmberFile.MakeNamesUnique();
|
||||
FillLibraryTree();
|
||||
SetEmber(0);
|
||||
}
|
||||
|
||||
void Fractorium::OnActionPasteXmlOver(bool checked) { m_Controller->PasteXmlOver(); }
|
||||
|
||||
/// <summary>
|
||||
/// Add reflective symmetry to the current ember.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::AddReflectiveSymmetry()
|
||||
{
|
||||
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
||||
|
||||
m_Ember.AddSymmetry(-1, m_Rand);
|
||||
m_Fractorium->FillXforms();
|
||||
combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final.
|
||||
UpdateRender();
|
||||
}
|
||||
|
||||
void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->AddReflectiveSymmetry(); }
|
||||
|
||||
/// <summary>
|
||||
/// Add rotational symmetry to the current ember.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::AddRotationalSymmetry()
|
||||
{
|
||||
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
||||
|
||||
m_Ember.AddSymmetry(2, m_Rand);
|
||||
m_Fractorium->FillXforms();
|
||||
combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final.
|
||||
UpdateRender();
|
||||
}
|
||||
|
||||
void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->AddRotationalSymmetry(); }
|
||||
|
||||
/// <summary>
|
||||
/// Add both reflective and rotational symmetry to the current ember.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::AddBothSymmetry()
|
||||
{
|
||||
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
||||
|
||||
m_Ember.AddSymmetry(-2, m_Rand);
|
||||
m_Fractorium->FillXforms();
|
||||
combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final.
|
||||
UpdateRender();
|
||||
}
|
||||
|
||||
void Fractorium::OnActionAddBothSymmetry(bool checked) { m_Controller->AddBothSymmetry(); }
|
||||
|
||||
/// <summary>
|
||||
/// Delete all but one xform in the current ember.
|
||||
/// Clear that xform's variations.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ClearFlame()
|
||||
{
|
||||
while (m_Ember.TotalXformCount() > 1)
|
||||
m_Ember.DeleteTotalXform(m_Ember.TotalXformCount() - 1);
|
||||
|
||||
if (m_Ember.XformCount() == 1)
|
||||
{
|
||||
if (Xform<T>* xform = m_Ember.GetXform(0))
|
||||
{
|
||||
xform->Clear();
|
||||
xform->ParentEmber(&m_Ember);
|
||||
}
|
||||
}
|
||||
|
||||
m_Fractorium->FillXforms();
|
||||
m_Fractorium->ui.CurrentXformCombo->setCurrentIndex(0);
|
||||
UpdateRender();
|
||||
}
|
||||
|
||||
void Fractorium::OnActionClearFlame(bool checked) { m_Controller->ClearFlame(); }
|
||||
|
||||
/// <summary>
|
||||
/// Re-render all previews.
|
||||
/// </summary>
|
||||
void Fractorium::OnActionRenderPreviews(bool checked)
|
||||
{
|
||||
m_Controller->RenderPreviews();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop all previews from being rendered. This is handy if the user
|
||||
/// opens a large file with many embers in it, such as an animation sequence.
|
||||
/// </summary>
|
||||
void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopPreviewRender(); }
|
||||
|
||||
/// <summary>
|
||||
/// Show the final render dialog as a modeless dialog to allow
|
||||
/// the user to minimize the main window while doing a lengthy final render.
|
||||
/// Note: The user probably should not be otherwise interacting with the main GUI
|
||||
/// while the final render is taking place.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionFinalRender(bool checked)
|
||||
{
|
||||
//First completely stop what the current rendering process is doing.
|
||||
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
|
||||
m_RenderStatusLabel->setText("Renderer stopped.");
|
||||
m_FinalRenderDialog->show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the final render dialog has been closed.
|
||||
/// </summary>
|
||||
/// <param name="result">Ignored</param>
|
||||
void Fractorium::OnFinalRenderClose(int result)
|
||||
{
|
||||
m_RenderStatusLabel->setText("Renderer starting...");
|
||||
StartRenderTimer();//Re-create the renderer and start rendering again.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the final options dialog.
|
||||
/// Restart rendering and sync options after the options dialog is dismissed with Ok.
|
||||
/// Called when the options dialog is finished with ok.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionOptions(bool checked)
|
||||
{
|
||||
if (m_OptionsDialog->exec())
|
||||
{
|
||||
//First completely stop what the current rendering process is doing.
|
||||
m_Controller->Shutdown();
|
||||
StartRenderTimer();//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer.
|
||||
m_Settings->sync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the about dialog.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnActionAbout(bool checked)
|
||||
{
|
||||
m_AboutDialog->exec();
|
||||
}
|
235
Source/Fractorium/FractoriumPalette.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
#define PALETTE_CELL_HEIGHT 16
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the palette UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitPaletteUI()
|
||||
{
|
||||
int spinHeight = 20, row = 0;
|
||||
QTableWidget* paletteTable = ui.PaletteListTable;
|
||||
QTableWidget* palettePreviewTable = ui.PalettePreviewTable;
|
||||
|
||||
connect(paletteTable, SIGNAL(cellClicked(int, int)), this, SLOT(OnPaletteCellClicked(int, int)), Qt::QueuedConnection);
|
||||
connect(paletteTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(OnPaletteCellDoubleClicked(int, int)), Qt::QueuedConnection);
|
||||
|
||||
//Palette adjustment table.
|
||||
QTableWidget* table = ui.PaletteAdjustTable;
|
||||
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//Split width over all columns evenly.
|
||||
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_PaletteHueSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_PaletteSaturationSpin, spinHeight, -100, 100, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_PaletteBrightnessSpin, spinHeight, -255, 255, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
|
||||
row = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a palette Xml file and populate the palette table with the contents.
|
||||
/// This will clear any previous contents.
|
||||
/// Called upon initialization, or controller type change.
|
||||
/// </summary>
|
||||
/// <param name="s">The full path to the palette file</param>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
template <typename T>
|
||||
bool FractoriumEmberController<T>::InitPaletteTable(string s)
|
||||
{
|
||||
QTableWidget* paletteTable = m_Fractorium->ui.PaletteListTable;
|
||||
QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
|
||||
|
||||
paletteTable->clear();
|
||||
|
||||
if (m_PaletteList.Init(s))//Default to this, but add an option later.//TODO
|
||||
{
|
||||
//Preview table.
|
||||
palettePreviewTable->setRowCount(1);
|
||||
palettePreviewTable->setColumnWidth(1, 260);//256 plus small margin on each side.
|
||||
QTableWidgetItem* previewNameCol = new QTableWidgetItem("");
|
||||
palettePreviewTable->setItem(0, 0, previewNameCol);
|
||||
QTableWidgetItem* previewPaletteItem = new QTableWidgetItem();
|
||||
palettePreviewTable->setItem(0, 1, previewPaletteItem);
|
||||
|
||||
//Palette list table.
|
||||
paletteTable->setRowCount(m_PaletteList.Count());
|
||||
paletteTable->setColumnWidth(1, 260);//256 plus small margin on each side.
|
||||
paletteTable->horizontalHeader()->setSectionsClickable(false);
|
||||
|
||||
//Headers get removed when clearing, so must re-create here.
|
||||
QTableWidgetItem* nameHeader = new QTableWidgetItem("Name");
|
||||
QTableWidgetItem* 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 (size_t i = 0; i < m_PaletteList.Count(); i++)
|
||||
{
|
||||
Palette<T>* p = m_PaletteList.GetPalette(i);
|
||||
vector<unsigned char> v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);
|
||||
QTableWidgetItem* nameCol = new QTableWidgetItem(p->m_Name.c_str());
|
||||
|
||||
nameCol->setToolTip(p->m_Name.c_str());
|
||||
paletteTable->setItem(i, 0, nameCol);
|
||||
|
||||
QImage image(v.data(), p->Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);
|
||||
QTableWidgetItem* paletteItem = new QTableWidgetItem();
|
||||
|
||||
paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image));
|
||||
paletteTable->setItem(i, 1, paletteItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<string> errors = m_PaletteList.ErrorReport();
|
||||
|
||||
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
|
||||
QMessageBox::critical(m_Fractorium, "Palette Read Error", "Could not load palette file, all images will be black. See info tab for details.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply adjustments to the current ember's palette.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ApplyPaletteToEmber()
|
||||
{
|
||||
int i, rot = 0;
|
||||
unsigned int blur = m_Fractorium->m_PaletteBlurSpin->value();
|
||||
unsigned int freq = m_Fractorium->m_PaletteFrequencySpin->value();
|
||||
double sat = (double)m_Fractorium->m_PaletteSaturationSpin->value() / 100.0;
|
||||
double brightness = (double)m_Fractorium->m_PaletteBrightnessSpin->value() / 255.0;
|
||||
double contrast = (double)(m_Fractorium->m_PaletteContrastSpin->value() > 0 ? (m_Fractorium->m_PaletteContrastSpin->value() * 2) : m_Fractorium->m_PaletteContrastSpin->value()) / 100.0;
|
||||
|
||||
m_Ember.m_Hue = (double)(m_Fractorium->m_PaletteHueSpin->value()) / 360.0;//This is the only palette adjustment value that gets saved with the ember, so just assign it here.
|
||||
|
||||
//Use the temp palette as the base and apply the adjustments gotten from the GUI and save the result in the ember palette.
|
||||
m_TempPalette.MakeAdjustedPalette(m_Ember.m_Palette, 0, m_Ember.m_Hue, sat, brightness, contrast, blur, freq);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use adjusted palette to update all related GUI controls with new color values.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
Xform<T>* xform = CurrentXform();
|
||||
QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
|
||||
QTableWidgetItem* previewPaletteItem = palettePreviewTable->item(0, 1);
|
||||
QString paletteName = QString::fromStdString(m_Ember.m_Palette.m_Name);
|
||||
|
||||
if (previewPaletteItem)//This can be null if the palette file was moved or corrupted.
|
||||
{
|
||||
//Use the adjusted palette to fill the preview palette control so the user can see the effects of applying the adjustements.
|
||||
vector<unsigned char> v = palette.MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);//Make the palette repeat for PALETTE_CELL_HEIGHT rows.
|
||||
|
||||
m_FinalPaletteImage = QImage(palette.Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);//Create a QImage out of it.
|
||||
memcpy(m_FinalPaletteImage.scanLine(0), v.data(), v.size() * sizeof(v[0]));//Memcpy the data in.
|
||||
QPixmap pixmap = QPixmap::fromImage(m_FinalPaletteImage);//Create a QPixmap out of the QImage.
|
||||
previewPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(QSize(pixmap.width(), palettePreviewTable->rowHeight(0) + 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));//Set the pixmap on the palette tab.
|
||||
SetPaletteRefTable(&pixmap);//Set the palette ref table on the xforms | color tab.
|
||||
|
||||
QTableWidgetItem* previewNameItem = palettePreviewTable->item(0, 0);
|
||||
previewNameItem->setText(paletteName);//Finally, set the name of the palette to be both the text and the tooltip.
|
||||
previewNameItem->setToolTip(paletteName);
|
||||
}
|
||||
|
||||
//Update the current xform's color and reset the rendering process.
|
||||
if (xform)
|
||||
XformColorIndexChanged(xform->m_ColorX, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply all adjustments to the selected palette, show it
|
||||
/// and assign it to the current ember.
|
||||
/// Called when any adjustment spinner is modified.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::PaletteAdjust()
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
ApplyPaletteToEmber();
|
||||
UpdateAdjustedPaletteGUI(m_Ember.m_Palette);
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnPaletteAdjust(int d) { m_Controller->PaletteAdjust(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the selected palette as the current one,
|
||||
/// applying any adjustments previously specified.
|
||||
/// Called when a palette cell is clicked. Unfortunately,
|
||||
/// this will get called twice on a double click when moving
|
||||
/// from one palette to another. It happens quickly so it shouldn't
|
||||
/// be too much of a problem.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="row">The table row clicked</param>
|
||||
/// <param name="col">The table col clicked</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::PaletteCellClicked(int row, int col)
|
||||
{
|
||||
Palette<T>* palette = m_PaletteList.GetPalette(row);
|
||||
QTableWidgetItem* nameItem = m_Fractorium->ui.PaletteListTable->item(row, 0);
|
||||
|
||||
if (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.
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnPaletteCellClicked(int row, int col)
|
||||
{
|
||||
if (m_PreviousPaletteRow != row)
|
||||
{
|
||||
m_Controller->PaletteCellClicked(row, col);
|
||||
m_PreviousPaletteRow = row;//Save for comparison on next click.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the selected palette as the current one,
|
||||
/// resetting any adjustments previously specified.
|
||||
/// Called when a palette cell is double clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="row">The table row clicked</param>
|
||||
/// <param name="col">The table col clicked</param>
|
||||
void Fractorium::OnPaletteCellDoubleClicked(int row, int col)
|
||||
{
|
||||
ResetPaletteControls();
|
||||
m_PreviousPaletteRow = -1;
|
||||
OnPaletteCellClicked(row, col);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the palette controls.
|
||||
/// Usually in response to a palette cell double click.
|
||||
/// </summary>
|
||||
void Fractorium::ResetPaletteControls()
|
||||
{
|
||||
m_PaletteHueSpin->SetValueStealth(0);
|
||||
m_PaletteSaturationSpin->SetValueStealth(0);
|
||||
m_PaletteBrightnessSpin->SetValueStealth(0);
|
||||
m_PaletteContrastSpin->SetValueStealth(0);
|
||||
m_PaletteBlurSpin->SetValueStealth(0);
|
||||
m_PaletteFrequencySpin->SetValueStealth(1);
|
||||
}
|
627
Source/Fractorium/FractoriumParams.cpp
Normal file
@ -0,0 +1,627 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the parameters UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitParamsUI()
|
||||
{
|
||||
int row = 0;
|
||||
int spinHeight = 20;
|
||||
vector<string> comboVals;
|
||||
QTableWidget* table = ui.ColorTable;
|
||||
|
||||
//Because QTableWidget does not allow for a single title bar/header
|
||||
//at the top of a multi-column table, the workaround hack is to just
|
||||
//make another single column table with no rows, and use the single
|
||||
//column header as the title bar. Then positioning it right above the table
|
||||
//that holds the data. Disallow selecting and resizing of the title bar.
|
||||
SetFixedTableHeader(ui.ColorTableHeader->horizontalHeader());
|
||||
SetFixedTableHeader(ui.GeometryTableHeader->horizontalHeader());
|
||||
SetFixedTableHeader(ui.FilterTableHeader->horizontalHeader());
|
||||
SetFixedTableHeader(ui.IterationTableHeader->horizontalHeader());
|
||||
|
||||
//Color.
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.05, 100, 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.1, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.1);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_VibrancySpin, spinHeight, 0, 1, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 1.0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_HighlightSpin, spinHeight, -1.0, 2.0, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHighlightPowerChanged(double)), true, -1.0, -1.0, -1.0);
|
||||
|
||||
m_BackgroundColorButton = new QPushButton("...", table);
|
||||
m_BackgroundColorButton->setMinimumWidth(21);
|
||||
m_BackgroundColorButton->setMaximumWidth(21);
|
||||
table->setCellWidget(row, 1, m_BackgroundColorButton);
|
||||
table->item(row, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
connect(m_BackgroundColorButton, SIGNAL(clicked(bool)), this, SLOT(OnBackgroundColorButtonClicked(bool)), Qt::QueuedConnection);
|
||||
row++;
|
||||
|
||||
comboVals.push_back("Step");
|
||||
comboVals.push_back("Linear");
|
||||
SetupCombo(table, this, row, 1, m_PaletteModeCombo, comboVals, SIGNAL(currentIndexChanged(int)), SLOT(OnPaletteModeComboCurrentIndexChanged(int)));
|
||||
|
||||
//Geometry.
|
||||
row = 0;
|
||||
table = ui.GeometryTable;
|
||||
SetupSpinner<SpinBox, int> (table, this, row, 1, m_WidthSpin, spinHeight, 10, 100000, 50, SIGNAL(valueChanged(int)), SLOT(OnWidthChanged(int)));
|
||||
SetupSpinner<SpinBox, int> (table, this, row, 1, m_HeightSpin, spinHeight, 10, 100000, 50, SIGNAL(valueChanged(int)), SLOT(OnHeightChanged(int)));
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_CenterXSpin, spinHeight, -10, 10, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterXChanged(double)), true, 0, 0, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_CenterYSpin, spinHeight, -10, 10, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterYChanged(double)), true, 0, 0, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_ScaleSpin, spinHeight, 10, 3000, 20, SIGNAL(valueChanged(double)), SLOT(OnScaleChanged(double)), true, 240, 240, 240);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_ZoomSpin, spinHeight, 0, 5, 0.2, SIGNAL(valueChanged(double)), SLOT(OnZoomChanged(double)), true, 0, 0, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_RotateSpin, spinHeight, -180, 180, 10, SIGNAL(valueChanged(double)), SLOT(OnRotateChanged(double)), true, 0, 0, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_ZPosSpin, spinHeight, -1000, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnZPosChanged(double)), true, 0, 1, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_PerspectiveSpin, spinHeight, -500, 500, 0.01, SIGNAL(valueChanged(double)), SLOT(OnPerspectiveChanged(double)), true, 0, 1, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_PitchSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(double)), SLOT(OnPitchChanged(double)), true, 0, 45, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_YawSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(double)), SLOT(OnYawChanged(double)), true, 0, 45, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DepthBlurSpin, spinHeight, -100, 100, 0.01, SIGNAL(valueChanged(double)), SLOT(OnDepthBlurChanged(double)), true, 0, 1, 0);
|
||||
m_WidthSpin->setEnabled(false);//Will programatically change these to match the window size, but the user should never be allowed to.
|
||||
m_HeightSpin->setEnabled(false);
|
||||
m_CenterXSpin->setDecimals(3);
|
||||
m_CenterYSpin->setDecimals(3);
|
||||
m_ZPosSpin->setDecimals(3);
|
||||
m_PerspectiveSpin->setDecimals(4);
|
||||
m_DepthBlurSpin->setDecimals(3);
|
||||
|
||||
//Filter.
|
||||
row = 0;
|
||||
table = ui.FilterTable;
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_SpatialFilterWidthSpin, spinHeight, 0.1, 10, 0.1, SIGNAL(valueChanged(double)), SLOT(OnSpatialFilterWidthChanged(double)), true, 1.0, 1.0, 1.0);
|
||||
|
||||
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_TemporalFilterWidthSpin, spinHeight, 1, 10, 1, SIGNAL(valueChanged(double)), SLOT(OnTemporalFilterWidthChanged(double)), true, 1);
|
||||
|
||||
comboVals = TemporalFilterCreator<float>::FilterTypes();
|
||||
SetupCombo(table, this, row, 1, m_TemporalFilterTypeCombo, comboVals, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnTemporalFilterTypeComboCurrentIndexChanged(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);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DEFilterMaxRadiusSpin, spinHeight, 0, 25, 1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterMaxRadiusWidthChanged(double)), true, 9.0, 9.0, 0);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DECurveSpin, spinHeight, 0.15, 5, 0.1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterCurveWidthChanged(double)), true, 0.4, 0.4, 0.4);
|
||||
|
||||
//Iteration.
|
||||
row = 0;
|
||||
table = ui.IterationTable;
|
||||
SetupSpinner<SpinBox, int> (table, this, row, 1, m_PassesSpin, spinHeight, 1, 3, 1, SIGNAL(valueChanged(int)), SLOT(OnPassesChanged(int)), true, 1, 1, 1);
|
||||
SetupSpinner<SpinBox, int> (table, this, row, 1, m_TemporalSamplesSpin, spinHeight, 1, 5000, 50, SIGNAL(valueChanged(int)), SLOT(OnTemporalSamplesChanged(int)), true, 1000);
|
||||
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_QualitySpin, spinHeight, 1, 200000, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, 10, 10, 10);
|
||||
SetupSpinner<SpinBox, int> (table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 1, 1, 1);
|
||||
|
||||
comboVals.clear();
|
||||
comboVals.push_back("Step");
|
||||
comboVals.push_back("Linear");
|
||||
SetupCombo(table, this, row, 1, m_AffineInterpTypeCombo, comboVals, SIGNAL(currentIndexChanged(int)), SLOT(OnAffineInterpTypeComboCurrentIndexChanged(int)));
|
||||
|
||||
comboVals.clear();
|
||||
comboVals.push_back("Linear");
|
||||
comboVals.push_back("Smooth");
|
||||
SetupCombo(table, this, row, 1, m_InterpTypeCombo, comboVals, SIGNAL(currentIndexChanged(int)), SLOT(OnInterpTypeComboCurrentIndexChanged(int)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Set the brightness to be used for calculating K1 and K2 for filtering and final accum.
|
||||
/// Called when brightness spinner is changed.
|
||||
/// Resets the rendering process to the filtering stage.
|
||||
/// </summary>
|
||||
/// <param name="d">The brightness</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::BrightnessChanged(double d) { Update([&] { m_Ember.m_Brightness = d; }, true, FILTER_AND_ACCUM); }
|
||||
void Fractorium::OnBrightnessChanged(double d) { m_Controller->BrightnessChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the gamma to be used for final accum.
|
||||
/// Called when gamma spinner is changed.
|
||||
/// Resets the rendering process if temporal samples is greater than 1,
|
||||
/// else if early clip is true, filter and accum, else final accum only.
|
||||
/// </summary>
|
||||
/// <param name="d">The gamma value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::GammaChanged(double d) { Update([&] { m_Ember.m_Gamma = d; }, true, m_Ember.m_TemporalSamples > 1 ? FULL_RENDER : (m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY)); }
|
||||
void Fractorium::OnGammaChanged(double d) { m_Controller->GammaChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the gamma threshold to be used for final accum.
|
||||
/// Called when gamma threshold spinner is changed.
|
||||
/// Resets the rendering process to the final accumulation stage.
|
||||
/// </summary>
|
||||
/// <param name="d">The gamma threshold</param>
|
||||
template <typename T> void FractoriumEmberController<T>::GammaThresholdChanged(double d) { Update([&] { m_Ember.m_GammaThresh = d; }, true, m_Ember.m_TemporalSamples > 1 ? FULL_RENDER : (m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY)); }
|
||||
void Fractorium::OnGammaThresholdChanged(double d) { m_Controller->GammaThresholdChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the vibrancy to be used for final accum.
|
||||
/// Called when vibrancy spinner is changed.
|
||||
/// Resets the rendering process to the final accumulation stage if temporal samples is 1, else full reset.
|
||||
/// </summary>
|
||||
/// <param name="d">The vibrancy</param>
|
||||
template <typename T> void FractoriumEmberController<T>::VibrancyChanged(double d) { Update([&] { m_Ember.m_Vibrancy = d; }, true, m_Ember.m_TemporalSamples > 1 ? FULL_RENDER : (m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY)); }
|
||||
void Fractorium::OnVibrancyChanged(double d) { m_Controller->VibrancyChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the highlight power to be used for final accum.
|
||||
/// Called when highlight power spinner is changed.
|
||||
/// Resets the rendering process to the final accumulation stage.
|
||||
/// </summary>
|
||||
/// <param name="d">The highlight power</param>
|
||||
template <typename T> void FractoriumEmberController<T>::HighlightPowerChanged(double d) { Update([&] { m_Ember.m_HighlightPower = d; }, true, m_Ember.m_TemporalSamples > 1 ? FULL_RENDER : (m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY)); }
|
||||
void Fractorium::OnHighlightPowerChanged(double d) { m_Controller->HighlightPowerChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Show the color selection dialog.
|
||||
/// Called when background color button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnBackgroundColorButtonClicked(bool checked)
|
||||
{
|
||||
m_ColorDialog->show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a new ember background color when the user accepts the color dialog.
|
||||
/// Also change the background and foreground colors of the color cell in the
|
||||
/// color params table.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to set, RGB in the 0-255 range</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::BackgroundChanged(const QColor& color)
|
||||
{
|
||||
Update([&]
|
||||
{
|
||||
int itemRow = 5;
|
||||
QTableWidget* colorTable = m_Fractorium->ui.ColorTable;
|
||||
colorTable->item(itemRow, 1)->setBackgroundColor(color);
|
||||
|
||||
QString r = QString::number(color.red());
|
||||
QString g = QString::number(color.green());
|
||||
QString b = QString::number(color.blue());
|
||||
|
||||
int threshold = 105;
|
||||
int delta = (color.red() * 0.299) + //Magic numbers gotten from a Stack Overflow post.
|
||||
(color.green() * 0.587) +
|
||||
(color.blue() * 0.114);
|
||||
|
||||
QColor textColor = (255 - delta < threshold) ? QColor(0, 0, 0) : QColor(255, 255, 255);
|
||||
colorTable->item(itemRow, 1)->setTextColor(textColor);
|
||||
colorTable->item(itemRow, 1)->setText("rgb(" + r + ", " + g + ", " + b + ")");
|
||||
|
||||
//Color is 0-255, normalize to 0-1.
|
||||
m_Ember.m_Background.r = color.red() / 255.0;
|
||||
m_Ember.m_Background.g = color.green() / 255.0;
|
||||
m_Ember.m_Background.b = color.blue() / 255.0;
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnColorSelected(const QColor& color) { m_Controller->BackgroundChanged(color); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the palette index interpolation mode.
|
||||
/// Called when palette mode combo box index is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the palette mode combo box</param>
|
||||
template <typename T> void FractoriumEmberController<T>::PaletteModeChanged(unsigned int i) { Update([&] { m_Ember.m_PaletteMode = i == 0 ? PALETTE_STEP : PALETTE_LINEAR; }); }
|
||||
void Fractorium::OnPaletteModeComboCurrentIndexChanged(int index) { m_Controller->PaletteModeChanged(index); }
|
||||
|
||||
/// <summary>
|
||||
/// Geometry.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder, do nothing.
|
||||
/// Dimensions are set automatically to match the dimensions of GLWidget.
|
||||
/// </summary>
|
||||
/// <param name="d">Ignored</param>
|
||||
void Fractorium::OnWidthChanged(int d) { }
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder, do nothing.
|
||||
/// Dimensions are set automatically to match the dimensions of GLWidget.
|
||||
/// </summary>
|
||||
/// <param name="d">Ignored</param>
|
||||
void Fractorium::OnHeightChanged(int d) { }
|
||||
|
||||
/// <summary>
|
||||
/// Set the x offset applied to the center of the image.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The x offset value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::CenterXChanged(double d) { Update([&] { m_Ember.m_CenterX = d; }); }
|
||||
void Fractorium::OnCenterXChanged(double d) { m_Controller->CenterXChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the y offset applied to the center of the image.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The y offset value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::CenterYChanged(double d) { Update([&] { m_Ember.m_CenterY = d; }); }
|
||||
void Fractorium::OnCenterYChanged(double d) { m_Controller->CenterYChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the scale (pixels per unit) value of the image.
|
||||
/// Note this will not increase the number of iters ran, but will degrade quality.
|
||||
/// To preserve quality, but exponentially increase iters, use zoom.
|
||||
/// Called when scale spinner is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The scale value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::ScaleChanged(double d) { Update([&] { m_Ember.m_PixelsPerUnit = d; }); }
|
||||
void Fractorium::OnScaleChanged(double d) { m_Controller->ScaleChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the zoom value of the image.
|
||||
/// Note this will increase the number of iters ran exponentially.
|
||||
/// To zoom in without increasing iters, but sacrifice quality, use scale.
|
||||
/// Called when zoom spinner is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The zoom value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::ZoomChanged(double d) { Update([&] { m_Ember.m_Zoom = d; }); }
|
||||
void Fractorium::OnZoomChanged(double d) { m_Controller->ZoomChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the angular rotation of the image.
|
||||
/// Called when rotate spinner is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The rotation in angles</param>
|
||||
template <typename T> void FractoriumEmberController<T>::RotateChanged(double d) { Update([&] { m_Ember.m_Rotate = d; }); }
|
||||
void Fractorium::OnRotateChanged(double d) { m_Controller->RotateChanged(d); }
|
||||
|
||||
template <typename T> void FractoriumEmberController<T>::ZPosChanged(double d) { Update([&] { m_Ember.m_CamZPos = d; }); }
|
||||
void Fractorium::OnZPosChanged(double d) { m_Controller->ZPosChanged(d); }
|
||||
|
||||
template <typename T> void FractoriumEmberController<T>::PerspectiveChanged(double d) { Update([&] { m_Ember.m_CamPerspective = d; }); }
|
||||
void Fractorium::OnPerspectiveChanged(double d) { m_Controller->PerspectiveChanged(d); }
|
||||
|
||||
template <typename T> void FractoriumEmberController<T>::PitchChanged(double d) { Update([&] { m_Ember.m_CamPitch = d * DEG_2_RAD; }); }
|
||||
void Fractorium::OnPitchChanged(double d) { m_Controller->PitchChanged(d); }
|
||||
|
||||
template <typename T> void FractoriumEmberController<T>::YawChanged(double d) { Update([&] { m_Ember.m_CamYaw = d * DEG_2_RAD; }); }
|
||||
void Fractorium::OnYawChanged(double d) { m_Controller->YawChanged(d); }
|
||||
|
||||
template <typename T> void FractoriumEmberController<T>::DepthBlurChanged(double d) { Update([&] { m_Ember.m_CamDepthBlur = d; }); }
|
||||
void Fractorium::OnDepthBlurChanged(double d) { m_Controller->DepthBlurChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Filter.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Set the spatial filter width.
|
||||
/// Called when the spatial filter width spinner is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The spatial filter width</param>
|
||||
template <typename T> void FractoriumEmberController<T>::SpatialFilterWidthChanged(double d) { Update([&] { m_Ember.m_SpatialFilterRadius = d; }); }//Must fully reset because it's used to create bounds.
|
||||
void Fractorium::OnSpatialFilterWidthChanged(double d) { m_Controller->SpatialFilterWidthChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the spatial filter type.
|
||||
/// Called when the spatial filter type combo box index is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="text">The spatial filter type</param>
|
||||
template <typename T> void FractoriumEmberController<T>::SpatialFilterTypeChanged(const QString& text) { Update([&] { m_Ember.m_SpatialFilterType = SpatialFilterCreator<T>::FromString(text.toStdString()); }); }//Must fully reset because it's used to create bounds.
|
||||
void Fractorium::OnSpatialFilterTypeComboCurrentIndexChanged(const QString& text) { m_Controller->SpatialFilterTypeChanged(text); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the temporal filter width to be used with animation.
|
||||
/// Called when the temporal filter width spinner is changed.
|
||||
/// Does not reset anything because this is only used for animation.
|
||||
/// In the future, when animation is implemented, this will have an effect.
|
||||
/// </summary>
|
||||
/// <param name="d">The temporal filter width</param>
|
||||
template <typename T> void FractoriumEmberController<T>::TemporalFilterWidthChanged(double d) { Update([&] { m_Ember.m_TemporalFilterWidth = d; }, true, NOTHING); }//Don't do anything until animation is implemented.
|
||||
void Fractorium::OnTemporalFilterWidthChanged(double d) { m_Controller->TemporalFilterWidthChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the temporal filter type to be used with animation.
|
||||
/// Called when the temporal filter combo box index is changed.
|
||||
/// Does not reset anything because this is only used for animation.
|
||||
/// In the future, when animation is implemented, this will have an effect.
|
||||
/// </summary>
|
||||
/// <param name="text">The name of the temporal filter</param>
|
||||
template <typename T> void FractoriumEmberController<T>::TemporalFilterTypeChanged(const QString& text) { Update([&] { m_Ember.m_TemporalFilterType = TemporalFilterCreator<T>::FromString(text.toStdString()); }, true, NOTHING); }//Don't do anything until animation is implemented.
|
||||
void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& text) { m_Controller->TemporalFilterTypeChanged(text); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the density estimation filter min radius value.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The min radius value</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::DEFilterMinRadiusWidthChanged(double d)
|
||||
{
|
||||
Update([&]
|
||||
{
|
||||
if (m_Fractorium->m_DEFilterMinRadiusSpin->value() > m_Fractorium->m_DEFilterMaxRadiusSpin->value())
|
||||
{
|
||||
m_Fractorium->m_DEFilterMinRadiusSpin->setValue(m_Fractorium->m_DEFilterMaxRadiusSpin->value() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Ember.m_MinRadDE = d;
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFilterMinRadiusWidthChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the density estimation filter max radius value.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The max radius value</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::DEFilterMaxRadiusWidthChanged(double d)
|
||||
{
|
||||
Update([&]
|
||||
{
|
||||
if (m_Fractorium->m_DEFilterMaxRadiusSpin->value() < m_Fractorium->m_DEFilterMinRadiusSpin->value())
|
||||
{
|
||||
m_Fractorium->m_DEFilterMaxRadiusSpin->setValue(m_Fractorium->m_DEFilterMinRadiusSpin->value() + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Ember.m_MaxRadDE = d;
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d) { m_Controller->DEFilterMaxRadiusWidthChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the density estimation filter curve value.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The curve value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::DEFilterCurveWidthChanged(double d) { Update([&] { m_Ember.m_CurveDE = d; }); }
|
||||
void Fractorium::OnDEFilterCurveWidthChanged(double d) { m_Controller->DEFilterCurveWidthChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Iteration.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Set the number of passes.
|
||||
/// This is a feature that is mostly useless and unused, and may even be removed soon.
|
||||
/// It should never be set to a value greater than 1.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The passes value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::PassesChanged(int i) { Update([&] { m_Ember.m_Passes = i; }); }
|
||||
void Fractorium::OnPassesChanged(int d) { m_Controller->PassesChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the temporal samples to be used with animation.
|
||||
/// Called when the temporal samples spinner is changed.
|
||||
/// Does not reset anything because this is only used for animation.
|
||||
/// In the future, when animation is implemented, this will have an effect.
|
||||
/// </summary>
|
||||
/// <param name="d">The temporal samples value</param>
|
||||
template <typename T> void FractoriumEmberController<T>::TemporalSamplesChanged(int i) { Update([&] { m_Ember.m_TemporalSamples = i; }, true, NOTHING); }//Don't do anything until animation is implemented.
|
||||
void Fractorium::OnTemporalSamplesChanged(int d) { m_Controller->TemporalSamplesChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the quality.
|
||||
/// 10 is good for interactive rendering on the CPU.
|
||||
/// 20-50 is good for OpenCL.
|
||||
/// Above 500 seems to offer little additional value for final renders.
|
||||
/// Called when the quality spinner is changed.
|
||||
/// If rendering is done, and the value is greater than the last value,
|
||||
/// the rendering process is continued, else it's reset.
|
||||
/// </summary>
|
||||
/// <param name="d">The quality in terms of iterations per pixel</param>
|
||||
template <typename T> void FractoriumEmberController<T>::QualityChanged(double d) { Update([&] { m_Ember.m_Quality = d; }, true, d > m_Ember.m_Quality ? KEEP_ITERATING : FULL_RENDER); }
|
||||
void Fractorium::OnQualityChanged(double d) { m_Controller->QualityChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the supersample.
|
||||
/// Note this will dramatically degrade performance, especially in
|
||||
/// OpenCL, while only giving a minor improvement in visual quality.
|
||||
/// Values above 2 add no noticeable difference.
|
||||
/// The user should only use this for a final render, or a quick preview, and then
|
||||
/// reset it back to 1 when done.
|
||||
/// Called when the supersample spinner is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The supersample value to set</param>
|
||||
template <typename T> void FractoriumEmberController<T>::SupersampleChanged(int d) { Update([&] { m_Ember.m_Supersample = d; }); }
|
||||
void Fractorium::OnSupersampleChanged(int d) { m_Controller->SupersampleChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the affine interpolation type.
|
||||
/// Does not reset anything because this is only used for animation.
|
||||
/// In the future, when animation is implemented, this will have an effect.
|
||||
/// Called when the affine interp type combo box index is changed.
|
||||
/// </summary>
|
||||
/// <param name="index">The index</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::AffineInterpTypeChanged(int i)
|
||||
{
|
||||
Update([&]
|
||||
{
|
||||
if (i == 0)
|
||||
m_Ember.m_AffineInterp = INTERP_LINEAR;
|
||||
else if (i == 1)
|
||||
m_Ember.m_AffineInterp = INTERP_LOG;
|
||||
else
|
||||
m_Ember.m_AffineInterp = INTERP_LINEAR;
|
||||
}, true, NOTHING);
|
||||
}
|
||||
|
||||
void Fractorium::OnAffineInterpTypeComboCurrentIndexChanged(int index) { m_Controller->AffineInterpTypeChanged(index); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the interpolation type.
|
||||
/// Does not reset anything because this is only used for animation.
|
||||
/// In the future, when animation is implemented, this will have an effect.
|
||||
/// Called when the interp type combo box index is changed.
|
||||
/// </summary>
|
||||
/// <param name="i">The index</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::InterpTypeChanged(int i)
|
||||
{
|
||||
Update([&]
|
||||
{
|
||||
if (i == 0)
|
||||
m_Ember.m_Interp = EMBER_INTERP_LINEAR;
|
||||
else if (i == 1)
|
||||
m_Ember.m_Interp = EMBER_INTERP_SMOOTH;
|
||||
else
|
||||
m_Ember.m_Interp = EMBER_INTERP_LINEAR;
|
||||
}, true, NOTHING);
|
||||
}
|
||||
|
||||
void Fractorium::OnInterpTypeComboCurrentIndexChanged(int index) { m_Controller->InterpTypeChanged(index); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the center.
|
||||
/// This updates the spinners as well as the current ember center.
|
||||
/// Resets the renering process.
|
||||
/// </summary>
|
||||
/// <param name="x">The x offset</param>
|
||||
/// <param name="y">The y offset</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SetCenter(double x, double y)
|
||||
{
|
||||
m_Ember.m_CenterX = x;
|
||||
m_Ember.m_CenterY = y;
|
||||
m_Fractorium->m_CenterXSpin->SetValueStealth(x);//Don't trigger a redraw twice.
|
||||
m_Fractorium->m_CenterYSpin->SetValueStealth(y);
|
||||
|
||||
if (m_Renderer.get())//On startup, this will be null at first because a resize takes place before the rendering thread is started.
|
||||
CenterXChanged(m_Ember.m_CenterX);//Trigger update using both new values.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the parameter tables and palette widgets with values from the current ember.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FillParamTablesAndPalette()
|
||||
{
|
||||
m_Fractorium->m_BrightnessSpin->SetValueStealth(m_Ember.m_Brightness);//Color.
|
||||
m_Fractorium->m_GammaSpin->SetValueStealth(m_Ember.m_Gamma);
|
||||
m_Fractorium->m_GammaThresholdSpin->SetValueStealth(m_Ember.m_GammaThresh);
|
||||
m_Fractorium->m_VibrancySpin->SetValueStealth(m_Ember.m_Vibrancy);
|
||||
m_Fractorium->m_HighlightSpin->SetValueStealth(m_Ember.m_HighlightPower);
|
||||
m_Fractorium->m_ColorDialog->setCurrentColor(QColor(m_Ember.m_Background.r * 255, m_Ember.m_Background.g * 255, m_Ember.m_Background.b * 255));
|
||||
m_Fractorium->ui.ColorTable->item(5, 1)->setBackgroundColor(m_Fractorium->m_ColorDialog->currentColor());
|
||||
m_Fractorium->m_PaletteModeCombo->SetCurrentIndexStealth((int)m_Ember.m_PaletteMode);
|
||||
m_Fractorium->m_WidthSpin->SetValueStealth(m_Ember.m_FinalRasW);//Geometry.
|
||||
m_Fractorium->m_HeightSpin->SetValueStealth(m_Ember.m_FinalRasH);
|
||||
m_Fractorium->m_CenterXSpin->SetValueStealth(m_Ember.m_CenterX);
|
||||
m_Fractorium->m_CenterYSpin->SetValueStealth(m_Ember.m_CenterY);
|
||||
m_Fractorium->m_ScaleSpin->SetValueStealth(m_Ember.m_PixelsPerUnit);
|
||||
m_Fractorium->m_RotateSpin->SetValueStealth(m_Ember.m_Rotate);
|
||||
m_Fractorium->m_ZPosSpin->SetValueStealth(m_Ember.m_CamZPos);
|
||||
m_Fractorium->m_PerspectiveSpin->SetValueStealth(m_Ember.m_CamPerspective);
|
||||
m_Fractorium->m_PitchSpin->SetValueStealth(m_Ember.m_CamPitch * RAD_2_DEG_T);
|
||||
m_Fractorium->m_YawSpin->SetValueStealth(m_Ember.m_CamYaw * RAD_2_DEG_T);
|
||||
m_Fractorium->m_DepthBlurSpin->SetValueStealth(m_Ember.m_CamDepthBlur);
|
||||
m_Fractorium->m_SpatialFilterWidthSpin->SetValueStealth(m_Ember.m_SpatialFilterRadius);//Filter.
|
||||
m_Fractorium->m_SpatialFilterTypeCombo->SetCurrentIndexStealth((int)m_Ember.m_SpatialFilterType);
|
||||
m_Fractorium->m_TemporalFilterWidthSpin->SetValueStealth(m_Ember.m_TemporalFilterWidth);
|
||||
m_Fractorium->m_TemporalFilterTypeCombo->SetCurrentIndexStealth((int)m_Ember.m_TemporalFilterType);
|
||||
m_Fractorium->m_DEFilterMinRadiusSpin->SetValueStealth(m_Ember.m_MinRadDE);
|
||||
m_Fractorium->m_DEFilterMaxRadiusSpin->SetValueStealth(m_Ember.m_MaxRadDE);
|
||||
m_Fractorium->m_DECurveSpin->SetValueStealth(m_Ember.m_CurveDE);
|
||||
m_Fractorium->m_PassesSpin->SetValueStealth(m_Ember.m_Passes);//Iteration.
|
||||
m_Fractorium->m_TemporalSamplesSpin->SetValueStealth(m_Ember.m_TemporalSamples);
|
||||
m_Fractorium->m_QualitySpin->SetValueStealth(m_Ember.m_Quality);
|
||||
m_Fractorium->m_SupersampleSpin->SetValueStealth(m_Ember.m_Supersample);
|
||||
m_Fractorium->m_AffineInterpTypeCombo->SetCurrentIndexStealth(m_Ember.m_AffineInterp);
|
||||
m_Fractorium->m_InterpTypeCombo->SetCurrentIndexStealth(m_Ember.m_Interp);
|
||||
|
||||
//Palette.
|
||||
m_Fractorium->ResetPaletteControls();
|
||||
m_Fractorium->m_PaletteHueSpin->SetValueStealth(NormalizeDeg180<double>(m_Ember.m_Hue * 360.0));//Convert -0.5 to 0.5 range to -180 - 180.
|
||||
|
||||
//Use -1 as a placeholder to mean either generate a random palette from the list or
|
||||
//to just use the values "as-is" without looking them up in the list.
|
||||
if (m_Ember.m_Palette.m_Index >= 0)
|
||||
{
|
||||
m_Fractorium->OnPaletteCellClicked(Clamp<int>(m_Ember.m_Palette.m_Index, 0, m_Fractorium->ui.PaletteListTable->rowCount() - 1), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
//An ember with an embedded palette was loaded, rather than one from the list, so assign it directly to the controls without applying adjustments.
|
||||
//Normally, temp palette is assigned whenever the user clicks on a palette cell. But since that is skipped here just make a copy of the ember's palette.
|
||||
m_TempPalette = m_Ember.m_Palette;
|
||||
UpdateAdjustedPaletteGUI(m_Ember.m_Palette);//Will clear name string since embedded palettes have no name. This will trigger a full render.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy all GUI widget values on the parameters tab to the passed in ember.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to copy values to.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ParamsToEmber(Ember<T>& ember)
|
||||
{
|
||||
QColor color = m_Fractorium->ui.ColorTable->item(5, 1)->backgroundColor();
|
||||
|
||||
ember.m_Brightness = m_Fractorium->m_BrightnessSpin->value();//Color.
|
||||
ember.m_Gamma = m_Fractorium->m_GammaSpin->value();
|
||||
ember.m_GammaThresh = m_Fractorium->m_GammaThresholdSpin->value();
|
||||
ember.m_Vibrancy = m_Fractorium->m_VibrancySpin->value();
|
||||
ember.m_HighlightPower = m_Fractorium->m_HighlightSpin->value();
|
||||
ember.m_Background.r = color.red() / 255.0;
|
||||
ember.m_Background.g = color.green() / 255.0;
|
||||
ember.m_Background.b = color.blue() / 255.0;
|
||||
ember.m_PaletteMode = (ePaletteMode)m_Fractorium->m_PaletteModeCombo->currentIndex();
|
||||
ember.m_FinalRasW = m_Fractorium->m_WidthSpin->value();//Geometry.
|
||||
ember.m_FinalRasH = m_Fractorium->m_HeightSpin->value();
|
||||
ember.m_CenterX = m_Fractorium->m_CenterXSpin->value();
|
||||
ember.m_CenterY = m_Fractorium->m_CenterYSpin->value();
|
||||
ember.m_PixelsPerUnit = m_Fractorium->m_ScaleSpin->value();
|
||||
ember.m_Rotate = m_Fractorium->m_RotateSpin->value();
|
||||
ember.m_CamZPos = m_Fractorium->m_ZPosSpin->value();
|
||||
ember.m_CamPerspective = m_Fractorium->m_PerspectiveSpin->value();
|
||||
ember.m_CamPitch = m_Fractorium->m_PitchSpin->value() * DEG_2_RAD_T;
|
||||
ember.m_CamYaw = m_Fractorium->m_YawSpin->value() * DEG_2_RAD_T;
|
||||
ember.m_CamDepthBlur = m_Fractorium->m_DepthBlurSpin->value();
|
||||
ember.m_SpatialFilterRadius = m_Fractorium->m_SpatialFilterWidthSpin->value();//Filter.
|
||||
ember.m_SpatialFilterType = (eSpatialFilterType)m_Fractorium->m_SpatialFilterTypeCombo->currentIndex();
|
||||
ember.m_TemporalFilterWidth = m_Fractorium->m_TemporalFilterWidthSpin->value();
|
||||
ember.m_TemporalFilterType = (eTemporalFilterType)m_Fractorium->m_TemporalFilterTypeCombo->currentIndex();
|
||||
ember.m_MinRadDE = m_Fractorium->m_DEFilterMinRadiusSpin->value();
|
||||
ember.m_MaxRadDE = m_Fractorium->m_DEFilterMaxRadiusSpin->value();
|
||||
ember.m_CurveDE = m_Fractorium->m_DECurveSpin->value();
|
||||
ember.m_Passes = m_Fractorium->m_PassesSpin->value();
|
||||
ember.m_TemporalSamples = m_Fractorium->m_TemporalSamplesSpin->value();
|
||||
ember.m_Quality = m_Fractorium->m_QualitySpin->value();
|
||||
ember.m_Supersample = m_Fractorium->m_SupersampleSpin->value();
|
||||
ember.m_AffineInterp = (eAffineInterp)m_Fractorium->m_AffineInterpTypeCombo->currentIndex();
|
||||
ember.m_Interp = (eInterp)m_Fractorium->m_InterpTypeCombo->currentIndex();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the rotation.
|
||||
/// This updates the spinner, optionally stealth.
|
||||
/// </summary>
|
||||
/// <param name="rot">The rotation value in angles to set</param>
|
||||
/// <param name="stealth">True if stealth to skip re-rendering, else false to trigger a new render</param>
|
||||
void Fractorium::SetRotation(double rot, bool stealth)
|
||||
{
|
||||
if (stealth)
|
||||
m_RotateSpin->SetValueStealth(rot);
|
||||
else
|
||||
m_RotateSpin->setValue(rot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the scale.
|
||||
/// This is the number of raster pixels that correspond to the distance
|
||||
/// between 0-1 in the cartesian plane. The higher the number, the more
|
||||
/// zoomed in the image is.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale value</param>
|
||||
void Fractorium::SetScale(double scale)
|
||||
{
|
||||
m_ScaleSpin->setValue(scale);
|
||||
}
|
1
Source/Fractorium/FractoriumPch.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "FractoriumPch.h"
|
44
Source/Fractorium/FractoriumPch.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#define GLM_FORCE_INLINE 1
|
||||
|
||||
#include "Renderer.h"
|
||||
#include "RendererCL.h"
|
||||
#include "VariationList.h"
|
||||
#include "OpenCLWrapper.h"
|
||||
#include "XmlToEmber.h"
|
||||
#include "EmberToXml.h"
|
||||
#include "SheepTools.h"
|
||||
#include "JpegUtils.h"
|
||||
#include "EmberCommon.h"
|
||||
#include <deque>
|
||||
#undef QT_OPENGL_ES_2//Make absolutely sure OpenGL ES is not used.
|
||||
#define QT_NO_OPENGL_ES_2
|
||||
#include <QtWidgets>
|
||||
#include <QLineEdit>
|
||||
#include <QSpinBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QPushButton>
|
||||
#include <QComboBox>
|
||||
#include <QColorDialog>
|
||||
#include <QTreeWidget>
|
||||
#include <QWheelEvent>
|
||||
#include <QItemDelegate>
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
#include <QGLWidget>
|
||||
#include <QOpenGLFunctions_2_0.h>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QFuture>
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
|
||||
#include "glm/glm.hpp"
|
||||
#include "glm/gtc/matrix_transform.hpp"
|
||||
#include "glm/gtc/type_ptr.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace EmberNs;
|
||||
using namespace EmberCLns;
|
630
Source/Fractorium/FractoriumRender.cpp
Normal file
@ -0,0 +1,630 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Return whether the render timer is running.
|
||||
/// </summary>
|
||||
/// <returns>True if running, else false.</returns>
|
||||
bool FractoriumEmberControllerBase::RenderTimerRunning()
|
||||
{
|
||||
return m_RenderTimer && m_RenderTimer->isActive();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the render timer.
|
||||
/// If a renderer has not been created yet, it will be created from the options.
|
||||
/// </summary>
|
||||
void FractoriumEmberControllerBase::StartRenderTimer()
|
||||
{
|
||||
if (m_RenderTimer)
|
||||
{
|
||||
UpdateRender();
|
||||
m_RenderTimer->start();
|
||||
m_RenderElapsedTimer.Tic();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the render timer after a short delay.
|
||||
/// If the timer is already running, stop it first.
|
||||
/// This is useful for stopping and restarting the render
|
||||
/// process in response to things like a window resize.
|
||||
/// </summary>
|
||||
void FractoriumEmberControllerBase::DelayedStartRenderTimer()
|
||||
{
|
||||
DeleteRenderer();
|
||||
|
||||
if (m_RenderRestartTimer)
|
||||
{
|
||||
m_RenderRestartTimer->setSingleShot(true);
|
||||
m_RenderRestartTimer->start(300);//Will stop the timer if it's already running, and start again.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the render timer and abort the rendering process.
|
||||
/// Optionally block until stopping is complete.
|
||||
/// </summary>
|
||||
/// <param name="wait">True to block, else false.</param>
|
||||
void FractoriumEmberControllerBase::StopRenderTimer(bool wait)
|
||||
{
|
||||
if (m_RenderTimer)
|
||||
m_RenderTimer->stop();
|
||||
|
||||
if (m_Renderer.get())
|
||||
m_Renderer->Abort();
|
||||
|
||||
if (wait)
|
||||
{
|
||||
while (m_Rendering || RenderTimerRunning() || (Renderer() && (!m_Renderer->Aborted() || m_Renderer->InRender())))
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop all timers, rendering and drawing and block until they are done.
|
||||
/// </summary>
|
||||
void FractoriumEmberControllerBase::Shutdown()
|
||||
{
|
||||
StopRenderTimer(true);
|
||||
|
||||
while(m_Fractorium->ui.GLDisplay->Drawing())
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the state of the renderer.
|
||||
/// Upon changing values, some intelligence is used to avoid blindly restarting the
|
||||
/// entire iteration proceess every time a value changes. This is because some values don't affect the
|
||||
/// iteration, and only affect filtering and final accumulation. They are broken into three categories:
|
||||
/// 1) Restart the entire process.
|
||||
/// 2) Log/density filter, then final accum.
|
||||
/// 3) Final accum only.
|
||||
/// 4) Continue iterating.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to take</param>
|
||||
void FractoriumEmberControllerBase::UpdateRender(eProcessAction action)
|
||||
{
|
||||
AddProcessAction(action);
|
||||
m_RenderElapsedTimer.Tic();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call Shutdown() then delete the renderer and clear the textures in the output window if there is one.
|
||||
/// </summary>
|
||||
void FractoriumEmberControllerBase::DeleteRenderer()
|
||||
{
|
||||
Shutdown();
|
||||
m_Renderer.reset();
|
||||
|
||||
if (GLController())
|
||||
GLController()->ClearWindow();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current contents of the GL window to a file.
|
||||
/// This could benefit from QImageWriter, however it's compression capabilities are
|
||||
/// severely lacking. A Png file comes out larger than a bitmap, so instead use the
|
||||
/// Png and Jpg wrapper functions from the command line programs.
|
||||
/// This will embed the id, url and nick fields from the options in the image comments.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path and filename</param>
|
||||
void FractoriumEmberControllerBase::SaveCurrentRender(QString filename)
|
||||
{
|
||||
if (filename != "")
|
||||
{
|
||||
bool b = false;
|
||||
unsigned int i, j;
|
||||
unsigned int width = m_Renderer->FinalRasW();
|
||||
unsigned int height = m_Renderer->FinalRasH();
|
||||
unsigned char* data = NULL;
|
||||
vector<unsigned char> vecRgb;
|
||||
QFileInfo fileInfo(filename);
|
||||
QString suffix = fileInfo.suffix();
|
||||
FractoriumSettings* settings = m_Fractorium->m_Settings;
|
||||
RendererCLBase* rendererCL = dynamic_cast<RendererCLBase*>(m_Renderer.get());
|
||||
|
||||
if (rendererCL && m_Renderer->PrepFinalAccumVector(m_FinalImage))
|
||||
{
|
||||
if (!rendererCL->ReadFinal(m_FinalImage.data()))
|
||||
{
|
||||
QMessageBox::critical(m_Fractorium, "GPU Read Error", "Could not read image from the GPU, aborting image save.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
data = m_FinalImage.data();//Png and channels = 4.
|
||||
|
||||
if ((suffix == "jpg" || suffix == "bmp") && m_Renderer->NumChannels() == 4)
|
||||
{
|
||||
EmberNs::RgbaToRgb(m_FinalImage, vecRgb, width, height);
|
||||
|
||||
data = vecRgb.data();
|
||||
}
|
||||
|
||||
string s = filename.toStdString();
|
||||
string id = settings->Id().toStdString();
|
||||
string url = settings->Url().toStdString();
|
||||
string nick = settings->Nick().toStdString();
|
||||
EmberImageComments comments = m_Renderer->ImageComments(0, false, true);
|
||||
|
||||
if (suffix == "png")
|
||||
b = WritePng(s.c_str(), data, width, height, 1, true, comments, id, url, nick);
|
||||
else if (suffix == "jpg")
|
||||
b = WriteJpeg(s.c_str(), data, width, height, 100, true, comments, id, url, nick);
|
||||
else if (suffix == "bmp")
|
||||
b = WriteBmp(s.c_str(), data, width, height);
|
||||
else
|
||||
QMessageBox::critical(m_Fractorium, "Save Failed", "Unrecognized format " + suffix + ", not saving.");
|
||||
|
||||
if (b)
|
||||
settings->SaveFolder(fileInfo.canonicalPath());
|
||||
else
|
||||
QMessageBox::critical(m_Fractorium, "Save Failed", "Could not save file, try saving to a different folder.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a process action to the list of actions to take.
|
||||
/// Called in response to the user changing something on the GUI.
|
||||
/// </summary>
|
||||
/// <param name="action">The action for the renderer to take</param>
|
||||
void FractoriumEmberControllerBase::AddProcessAction(eProcessAction action)
|
||||
{
|
||||
m_Cs.Enter();
|
||||
m_ProcessActions.push_back(action);
|
||||
|
||||
if (m_Renderer.get())
|
||||
m_Renderer->Abort();
|
||||
|
||||
m_Cs.Leave();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Condense and clear the process actions into a single action and return.
|
||||
/// Many actions may be specified, but only the one requiring the greatest amount
|
||||
/// of processing matters. Extract and return the greatest and clear the vector.
|
||||
/// </summary>
|
||||
/// <returns>The most significant processing action desired</returns>
|
||||
eProcessAction FractoriumEmberControllerBase::CondenseAndClearProcessActions()
|
||||
{
|
||||
m_Cs.Enter();
|
||||
eProcessAction action = NOTHING;
|
||||
|
||||
for (size_t i = 0; i < m_ProcessActions.size(); i++)
|
||||
if (m_ProcessActions[i] > action)
|
||||
action = m_ProcessActions[i];
|
||||
|
||||
m_ProcessActions.clear();
|
||||
m_Cs.Leave();
|
||||
return action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Render progress callback function to update progress bar.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember currently being rendered</param>
|
||||
/// <param name="foo">An extra dummy parameter</param>
|
||||
/// <param name="fraction">The progress fraction from 0-100</param>
|
||||
/// <param name="stage">The stage of iteration. 1 is iterating, 2 is density filtering, 2 is final accumulation.</param>
|
||||
/// <param name="etaMs">The estimated milliseconds to completion of the current stage</param>
|
||||
/// <returns>0 if the user has changed anything on the GUI, else 1 to continue rendering.</returns>
|
||||
template <typename T>
|
||||
int FractoriumEmberController<T>::ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs)
|
||||
{
|
||||
QString status;
|
||||
|
||||
m_Fractorium->m_ProgressBar->setValue((int)fraction);//Only really applies to iter and filter, because final accum only gives progress 0 and 100.
|
||||
|
||||
if (stage == 0)
|
||||
status = "Iterating";
|
||||
else if (stage == 1)
|
||||
status = "Density Filtering";
|
||||
else if (stage == 2)
|
||||
status = "Spatial Filtering + Final Accumulation";
|
||||
|
||||
m_Fractorium->m_RenderStatusLabel->setText(status);
|
||||
return m_ProcessActions.empty() ? 1 : 0;//If they've done anything, abort.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the undo list as well as the undo/redo index and state.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ClearUndo()
|
||||
{
|
||||
m_UndoIndex = 0;
|
||||
m_UndoList.clear();
|
||||
m_EditState = REGULAR_EDIT;
|
||||
m_LastEditWasUndoRedo = false;
|
||||
m_Fractorium->ui.ActionUndo->setEnabled(false);
|
||||
m_Fractorium->ui.ActionRedo->setEnabled(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main rendering function.
|
||||
/// Called whenever the event loop is idle.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
bool FractoriumEmberController<T>::Render()
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
m_Rendering = true;
|
||||
GLWidget* gl = m_Fractorium->ui.GLDisplay;
|
||||
eProcessAction action = CondenseAndClearProcessActions();
|
||||
|
||||
//Set dimensions first.
|
||||
if ((m_Ember.m_FinalRasW != gl->width() ||
|
||||
m_Ember.m_FinalRasH != gl->height()))
|
||||
{
|
||||
m_Ember.SetSizeAndAdjustScale(gl->width(), gl->height(), false, SCALE_WIDTH);
|
||||
m_Fractorium->m_ScaleSpin->SetValueStealth(m_Ember.m_PixelsPerUnit);
|
||||
action = FULL_RENDER;
|
||||
}
|
||||
|
||||
//Force temporal samples to always be 1. Perhaps change later when animation is implemented.
|
||||
m_Ember.m_TemporalSamples = 1;
|
||||
|
||||
//Take care of solo xforms and set the current ember and action.
|
||||
if (action != NOTHING)
|
||||
{
|
||||
int i, solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt();
|
||||
|
||||
if (solo != -1)
|
||||
{
|
||||
m_TempOpacities.resize(m_Ember.TotalXformCount());
|
||||
|
||||
for (i = 0; i < m_Ember.TotalXformCount(); i++)
|
||||
{
|
||||
m_TempOpacities[i] = m_Ember.GetTotalXform(i)->m_Opacity;
|
||||
m_Ember.GetTotalXform(i)->m_Opacity = i == solo ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_Renderer->SetEmber(m_Ember, action);
|
||||
|
||||
if (solo != -1)
|
||||
{
|
||||
for (i = 0; i < m_Ember.TotalXformCount(); i++)
|
||||
{
|
||||
m_Ember.GetTotalXform(i)->m_Opacity = m_TempOpacities[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Determining if a completely new rendering process is being started.
|
||||
bool iterBegin = ProcessState() == NONE;
|
||||
|
||||
if (iterBegin)
|
||||
{
|
||||
if (m_Renderer->RendererType() == CPU_RENDERER)
|
||||
m_SubBatchCount = m_Fractorium->m_Settings->CpuSubBatch();
|
||||
else if (m_Renderer->RendererType() == OPENCL_RENDERER)
|
||||
m_SubBatchCount = m_Fractorium->m_Settings->OpenCLSubBatch();
|
||||
|
||||
m_Fractorium->m_ProgressBar->setValue(0);
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Starting");
|
||||
}
|
||||
|
||||
//If the rendering process hasn't finished, render with the current specified action.
|
||||
if (ProcessState() != ACCUM_DONE)
|
||||
{
|
||||
//if (m_Renderer->Run(m_FinalImage, 0) == RENDER_OK)//Full, non-incremental render for debugging.
|
||||
if (m_Renderer->Run(m_FinalImage, 0, m_SubBatchCount, iterBegin) == RENDER_OK)//Force output on iterBegin.
|
||||
{
|
||||
//The amount to increment sub batch while rendering proceeds is purely empirical.
|
||||
//Change later if better values can be derived/observed.
|
||||
if (m_Renderer->RendererType() == OPENCL_RENDERER)
|
||||
{
|
||||
if (m_SubBatchCount < 3)//More than 3 with OpenCL gives a sluggish UI.
|
||||
m_SubBatchCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_SubBatchCount < 5)
|
||||
m_SubBatchCount++;
|
||||
else if (m_SubBatchCount < 105)//More than 105 with CPU gives a sluggish UI.
|
||||
m_SubBatchCount += 25;
|
||||
}
|
||||
|
||||
//Rendering has finished, update final stats.
|
||||
if (ProcessState() == ACCUM_DONE)
|
||||
{
|
||||
EmberStats stats = m_Renderer->Stats();
|
||||
|
||||
m_Fractorium->m_ProgressBar->setValue(100);
|
||||
QString iters = QLocale(QLocale::English).toString(stats.m_Iters);
|
||||
QString scaledQuality = QString::number((unsigned int)m_Renderer->ScaledQuality());
|
||||
string renderTime = m_RenderElapsedTimer.Format(m_RenderElapsedTimer.Toc());
|
||||
|
||||
//Only certain status can be reported with OpenCL.
|
||||
if (m_Renderer->RendererType() == OPENCL_RENDERER)
|
||||
{
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Total time: " + QString::fromStdString(renderTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
double percent = (double)stats.m_Badvals / (double)stats.m_Iters;
|
||||
QString badVals = QLocale(QLocale::English).toString(stats.m_Badvals);
|
||||
QString badPercent = QLocale(QLocale::English).toString(percent * 100, 'f', 2);
|
||||
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Bad values: " + badVals + " (" + badPercent + "%). Total time: " + QString::fromStdString(renderTime));
|
||||
}
|
||||
|
||||
if (m_LastEditWasUndoRedo && (m_UndoIndex == m_UndoList.size() - 1))//Traversing through undo list, reached the end, so put back in regular edit mode.
|
||||
{
|
||||
m_EditState = REGULAR_EDIT;
|
||||
}
|
||||
else if (m_EditState == REGULAR_EDIT)//Regular edit, just add to the end of the undo list.
|
||||
{
|
||||
m_UndoList.push_back(m_Ember);
|
||||
m_UndoIndex = m_UndoList.size() - 1;
|
||||
m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1);
|
||||
m_Fractorium->ui.ActionRedo->setEnabled(false);
|
||||
|
||||
if (m_UndoList.size() >= UNDO_SIZE)
|
||||
m_UndoList.pop_front();
|
||||
}
|
||||
else if (!m_LastEditWasUndoRedo && m_UndoIndex != m_UndoList.size() - 1)//They were in the middle of the undo list, then did a manual edit, so clear the undo list.
|
||||
{
|
||||
ClearUndo();
|
||||
m_UndoList.push_back(m_Ember);
|
||||
}
|
||||
|
||||
m_LastEditWasUndoRedo = false;
|
||||
m_Fractorium->UpdateHistogramBounds();//Mostly of engineering interest.
|
||||
}
|
||||
|
||||
//Update the GL window on start because the output will be forced.
|
||||
//Update it on finish because the rendering process is completely done.
|
||||
if (iterBegin || ProcessState() == ACCUM_DONE)
|
||||
{
|
||||
if (m_FinalImage.size() == m_Renderer->FinalBufferSize())//Make absolutely sure the correct amount of data is passed.
|
||||
gl->repaint();
|
||||
|
||||
//Uncomment for debugging kernel build and execution errors.
|
||||
//m_Fractorium->ui.InfoRenderingTextEdit->setText(QString::fromStdString(m_Fractorium->m_Wrapper.DumpInfo()));
|
||||
//if (RendererCL<T>* rendererCL = dynamic_cast<RendererCL<T>*>(m_Renderer.get()))
|
||||
// m_Fractorium->ui.InfoRenderingTextEdit->setText(QString::fromStdString(rendererCL->IterKernel()));
|
||||
}
|
||||
}
|
||||
else//Something went very wrong, show error report.
|
||||
{
|
||||
vector<string> errors = m_Renderer->ErrorReport();
|
||||
|
||||
success = false;
|
||||
m_FailedRenders++;
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Rendering failed, see info tab. Try changing parameters.");
|
||||
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoRenderingTextEdit);
|
||||
m_Renderer->ClearErrorReport();
|
||||
|
||||
if (m_FailedRenders >= 3)
|
||||
{
|
||||
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.data(), 0, m_FinalImage.size());
|
||||
|
||||
if (m_Renderer->RendererType() == OPENCL_RENDERER)
|
||||
((RendererCL<T>*)m_Renderer.get())->ClearFinal();
|
||||
|
||||
m_GLController->ClearWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Upon finishing, or having nothing to do, rest.
|
||||
if (ProcessState() == ACCUM_DONE)
|
||||
QThread::msleep(1);
|
||||
|
||||
m_Rendering = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop rendering and initialize a new renderer, using the specified type.
|
||||
/// Rendering will be left in a stopped state. The caller is responsible for restarting the render loop again.
|
||||
/// </summary>
|
||||
/// <param name="renderType">The type of render to create</param>
|
||||
/// <param name="platform">The index platform of the platform to use</param>
|
||||
/// <param name="device">The index device of the device to use</param>
|
||||
/// <param name="outputTexID">The texture ID of the shared OpenGL texture if shared</param>
|
||||
/// <param name="shared">True if shared with OpenGL, else false. Default: true.</param>
|
||||
/// <returns>True if nothing went wrong, else false.</returns>
|
||||
template <typename T>
|
||||
bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, unsigned int platform, unsigned int device, bool shared)
|
||||
{
|
||||
bool ok = true;
|
||||
FractoriumSettings* s = m_Fractorium->m_Settings;
|
||||
GLWidget* gl = m_Fractorium->ui.GLDisplay;
|
||||
|
||||
if (!m_Renderer.get() || (m_Renderer->RendererType() != renderType) || (m_Platform != platform) || (m_Device != device))
|
||||
{
|
||||
EmberReport emberReport;
|
||||
vector<string> errorReport;
|
||||
|
||||
DeleteRenderer();//Delete the renderer and refresh the textures.
|
||||
//Before starting, must take care of allocations.
|
||||
gl->Allocate(true);//Forcing a realloc of the texture is necessary on AMD, but not on nVidia.
|
||||
m_Renderer = auto_ptr<EmberNs::RendererBase>(::CreateRenderer<T, T>(renderType, platform, device, shared, gl->OutputTexID(), emberReport));
|
||||
errorReport = emberReport.ErrorReport();
|
||||
|
||||
if (errorReport.empty())
|
||||
{
|
||||
m_Platform = platform;//Store values for re-creation later on.
|
||||
m_Device = device;
|
||||
m_OutputTexID = gl->OutputTexID();
|
||||
m_Shared = shared;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
QMessageBox::critical(m_Fractorium, "Renderer Creation Error", "Could not create requested renderer, fallback CPU renderer created. See info tab for details.");
|
||||
m_Fractorium->ErrorReportToQTextEdit(errorReport, m_Fractorium->ui.InfoRenderingTextEdit);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Renderer.get())
|
||||
{
|
||||
m_RenderType = m_Renderer->RendererType();
|
||||
|
||||
if (m_RenderType == OPENCL_RENDERER && m_Fractorium->m_QualitySpin->value() < 30)
|
||||
m_Fractorium->m_QualitySpin->setValue(30);
|
||||
|
||||
m_Renderer->Callback(this);
|
||||
m_Renderer->NumChannels(4);//Always using 4 since the GL texture is RGBA.
|
||||
m_Renderer->ReclaimOnResize(true);
|
||||
m_Renderer->SetEmber(m_Ember);//Give it an initial ember, will be updated many times later.
|
||||
m_Renderer->EarlyClip(s->EarlyClip());
|
||||
m_Renderer->ThreadCount(s->ThreadCount());
|
||||
m_Renderer->Transparency(s->Transparency());
|
||||
|
||||
if (m_Renderer->RendererType() == CPU_RENDERER)
|
||||
m_Renderer->InteractiveFilter(s->CpuDEFilter() ? FILTER_DE : FILTER_LOG);
|
||||
else
|
||||
m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? FILTER_DE : FILTER_LOG);
|
||||
|
||||
m_FailedRenders = 0;
|
||||
m_RenderElapsedTimer.Tic();
|
||||
//Leave rendering in a stopped state. The caller is responsible for restarting the render loop again.
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
QMessageBox::critical(m_Fractorium, "Renderer Creation Error", "Creating a basic CPU renderer failed, something is catastrophically wrong. Exiting program.");
|
||||
QApplication::quit();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new renderer from the options.
|
||||
/// </summary>
|
||||
/// <returns>True if nothing went wrong, else false.</returns>
|
||||
bool Fractorium::CreateRendererFromOptions()
|
||||
{
|
||||
bool ok = true;
|
||||
bool useOpenCL = m_Wrapper.CheckOpenCL() && m_Settings->OpenCL();
|
||||
|
||||
//The most important option to process is what kind of renderer is desired, so do it first.
|
||||
if (!m_Controller->CreateRenderer(useOpenCL ? OPENCL_RENDERER : CPU_RENDERER,
|
||||
m_Settings->PlatformIndex(),
|
||||
m_Settings->DeviceIndex()))
|
||||
{
|
||||
//If using OpenCL, will only get here if creating RendererCL failed, but creating a backup CPU Renderer succeeded.
|
||||
QMessageBox::critical(this, "Renderer Creation Error", "Error creating renderer, most likely a GPU problem. Using CPU instead.");
|
||||
m_Settings->OpenCL(false);
|
||||
m_OptionsDialog->ui.OpenCLCheckBox->setChecked(false);
|
||||
m_FinalRenderDialog->ui.FinalRenderOpenCLCheckBox->setChecked(false);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new controller from the options.
|
||||
/// This does not create the internal renderer or start the timers.
|
||||
/// </summary>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
bool Fractorium::CreateControllerFromOptions()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
size_t size =
|
||||
#ifdef DO_DOUBLE
|
||||
m_Settings->Double() ? sizeof(double) :
|
||||
#endif
|
||||
sizeof(float);
|
||||
|
||||
if (!m_Controller.get() || (m_Controller->SizeOfT() != size))
|
||||
{
|
||||
double hue = m_PaletteHueSpin->value();
|
||||
double sat = m_PaletteSaturationSpin->value();
|
||||
double bright = m_PaletteBrightnessSpin->value();
|
||||
double con = m_PaletteContrastSpin->value();
|
||||
double blur = m_PaletteBlurSpin->value();
|
||||
double freq = m_PaletteFrequencySpin->value();
|
||||
#ifdef DO_DOUBLE
|
||||
Ember<double> ed;
|
||||
EmberFile<double> efd;
|
||||
Palette<double> tempPalette;
|
||||
#else
|
||||
Ember<float> ed;
|
||||
EmberFile<float> efd;
|
||||
Palette<float> tempPalette;
|
||||
#endif
|
||||
QModelIndex index = ui.LibraryTree->currentIndex();
|
||||
|
||||
//First check if a controller has already been created, and if so, save its embers and gracefully shut it down.
|
||||
if (m_Controller.get())
|
||||
{
|
||||
m_Controller->CopyTempPalette(tempPalette);//Convert float to double or save double verbatim;
|
||||
m_Controller->CopyEmber(ed);
|
||||
m_Controller->CopyEmberFile(efd);
|
||||
m_Controller->Shutdown();
|
||||
}
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
if (m_Settings->Double())
|
||||
m_Controller = auto_ptr<FractoriumEmberControllerBase>(new FractoriumEmberController<double>(this));
|
||||
else
|
||||
#endif
|
||||
m_Controller = auto_ptr<FractoriumEmberControllerBase>(new FractoriumEmberController<float>(this));
|
||||
|
||||
//Restore the ember and ember file.
|
||||
if (m_Controller.get())
|
||||
{
|
||||
m_Controller->SetEmber(ed);//Convert float to double or set double verbatim;
|
||||
m_Controller->SetEmberFile(efd);
|
||||
|
||||
//Template specific palette table and variations tree setup in controller constructor, but
|
||||
//must manually setup the library tree here because it's after the embers were assigned.
|
||||
m_Controller->FillLibraryTree(index.row());//Passing row re-selects the item that was previously selected.
|
||||
m_Controller->SetTempPalette(tempPalette);//Restore palette.
|
||||
m_PaletteHueSpin->SetValueStealth(hue);
|
||||
m_PaletteSaturationSpin->SetValueStealth(sat);
|
||||
m_PaletteBrightnessSpin->SetValueStealth(bright);
|
||||
m_PaletteContrastSpin->SetValueStealth(con);
|
||||
m_PaletteBlurSpin->SetValueStealth(blur);
|
||||
m_PaletteFrequencySpin->SetValueStealth(freq);
|
||||
m_Controller->PaletteAdjust();//Fills in the palette.
|
||||
}
|
||||
}
|
||||
|
||||
return m_Controller.get() != NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the render timer.
|
||||
/// If a renderer has not been created yet, or differs form the options, it will first be created from the options.
|
||||
/// </summary>
|
||||
void Fractorium::StartRenderTimer()
|
||||
{
|
||||
//Starting the render timer, either for the first time
|
||||
//or from a paused state, such as resizing or applying new options.
|
||||
CreateControllerFromOptions();
|
||||
|
||||
if (m_Controller.get())
|
||||
{
|
||||
//On program startup, the renderer does not get initialized until now.
|
||||
CreateRendererFromOptions();
|
||||
|
||||
if (m_Controller->Renderer())
|
||||
m_Controller->StartRenderTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Idle timer event which calls the controller's Render() function.
|
||||
/// </summary>
|
||||
void Fractorium::IdleTimer() { m_Controller->Render(); }
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper to determine if the controllers have been properly initialized.
|
||||
/// </summary>
|
||||
/// <returns>True if the ember controller and GL controllers are both not NULL, else false.</returns>
|
||||
bool Fractorium::ControllersOk() { return m_Controller.get() && m_Controller->GLController(); }
|
239
Source/Fractorium/FractoriumSettings.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "FractoriumSettings.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that passes the parent to the base and sets up reasonable defaults
|
||||
/// if the settings file was not present or corrupted.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
FractoriumSettings::FractoriumSettings(QObject* parent)
|
||||
: QSettings(QSettings::IniFormat, QSettings::UserScope, "Fractorium", "Fractorium", parent)
|
||||
{
|
||||
EnsureDefaults();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure options have reasonable values in them first.
|
||||
/// </summary>
|
||||
void FractoriumSettings::EnsureDefaults()
|
||||
{
|
||||
if (FinalWidth() == 0)
|
||||
FinalWidth(1920);
|
||||
|
||||
if (FinalHeight() == 0)
|
||||
FinalHeight(1080);
|
||||
|
||||
if (FinalQuality() == 0)
|
||||
FinalQuality(1000);
|
||||
|
||||
if (FinalTemporalSamples() == 0)
|
||||
FinalTemporalSamples(1000);
|
||||
|
||||
if (FinalSupersample() == 0)
|
||||
FinalSupersample(2);
|
||||
|
||||
if (XmlWidth() == 0)
|
||||
XmlWidth(1920);
|
||||
|
||||
if (XmlHeight() == 0)
|
||||
XmlHeight(1080);
|
||||
|
||||
if (XmlTemporalSamples() == 0)
|
||||
XmlTemporalSamples(1000);
|
||||
|
||||
if (XmlQuality() == 0)
|
||||
XmlQuality(1000);
|
||||
|
||||
if (XmlSupersample() == 0)
|
||||
XmlSupersample(2);
|
||||
|
||||
if (ThreadCount() == 0 || ThreadCount() > Timing::ProcessorCount())
|
||||
ThreadCount(max(1, 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());
|
||||
|
||||
if (CpuSubBatch() < 1)
|
||||
CpuSubBatch(10);
|
||||
|
||||
if (OpenCLSubBatch() < 1)
|
||||
OpenCLSubBatch(1);
|
||||
|
||||
//There normally wouldn't be any more than 10 OpenCL platforms and devices
|
||||
//on the system, so if a value greater than that is read, then the settings file
|
||||
//was corrupted.
|
||||
if (PlatformIndex() > 10)
|
||||
PlatformIndex(0);
|
||||
|
||||
if (DeviceIndex() > 10)
|
||||
DeviceIndex(0);
|
||||
|
||||
if (FinalScale() > SCALE_HEIGHT)
|
||||
FinalScale(0);
|
||||
|
||||
if (FinalPlatformIndex() > 10)
|
||||
FinalPlatformIndex(0);
|
||||
|
||||
if (FinalDeviceIndex() > 10)
|
||||
FinalDeviceIndex(0);
|
||||
|
||||
if (OpenXmlExt() == "")
|
||||
OpenXmlExt("Flame (*.flame)");
|
||||
|
||||
if (SaveXmlExt() == "")
|
||||
SaveXmlExt("Flame (*.flame)");
|
||||
|
||||
if (OpenImageExt() == "")
|
||||
OpenImageExt("Png (*.png)");
|
||||
|
||||
if (SaveImageExt() == "")
|
||||
SaveImageExt("Png (*.png)");
|
||||
|
||||
if (FinalDoAllExt() != "jpg" && FinalDoAllExt() != "png")
|
||||
FinalDoAllExt("png");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interactive renderer settings.
|
||||
/// </summary>
|
||||
|
||||
bool FractoriumSettings::EarlyClip() { return value(EARLYCLIP).toBool(); }
|
||||
void FractoriumSettings::EarlyClip(bool b) { setValue(EARLYCLIP, b); }
|
||||
|
||||
bool FractoriumSettings::Transparency() { return value(TRANSPARENCY).toBool(); }
|
||||
void FractoriumSettings::Transparency(bool b) { setValue(TRANSPARENCY, b); }
|
||||
|
||||
bool FractoriumSettings::Double() { return value(DOUBLEPRECISION).toBool(); }
|
||||
void FractoriumSettings::Double(bool b) { setValue(DOUBLEPRECISION, b); }
|
||||
|
||||
bool FractoriumSettings::OpenCL() { return value(OPENCL).toBool(); }
|
||||
void FractoriumSettings::OpenCL(bool b) { setValue(OPENCL, b); }
|
||||
|
||||
unsigned int FractoriumSettings::PlatformIndex() { return value(PLATFORMINDEX).toUInt(); }
|
||||
void FractoriumSettings::PlatformIndex(unsigned int i) { setValue(PLATFORMINDEX, i); }
|
||||
|
||||
unsigned int FractoriumSettings::DeviceIndex() { return value(DEVICEINDEX).toUInt(); }
|
||||
void FractoriumSettings::DeviceIndex(unsigned int i) { setValue(DEVICEINDEX, i); }
|
||||
|
||||
unsigned int FractoriumSettings::ThreadCount() { return value(THREADCOUNT).toUInt(); }
|
||||
void FractoriumSettings::ThreadCount(unsigned int i) { setValue(THREADCOUNT, i); }
|
||||
|
||||
bool FractoriumSettings::CpuDEFilter() { return value(CPUDEFILTER).toBool(); }
|
||||
void FractoriumSettings::CpuDEFilter(bool b) { setValue(CPUDEFILTER, b); }
|
||||
|
||||
bool FractoriumSettings::OpenCLDEFilter() { return value(OPENCLDEFILTER).toBool(); }
|
||||
void FractoriumSettings::OpenCLDEFilter(bool b) { setValue(OPENCLDEFILTER, b); }
|
||||
|
||||
unsigned int FractoriumSettings::CpuSubBatch() { return value(CPUSUBBATCH).toUInt(); }
|
||||
void FractoriumSettings::CpuSubBatch(unsigned int b) { setValue(CPUSUBBATCH, b); }
|
||||
|
||||
unsigned int FractoriumSettings::OpenCLSubBatch() { return value(OPENCLSUBBATCH).toUInt(); }
|
||||
void FractoriumSettings::OpenCLSubBatch(unsigned int b) { setValue(OPENCLSUBBATCH, b); }
|
||||
|
||||
/// <summary>
|
||||
/// Final render settings.
|
||||
/// </summary>
|
||||
|
||||
bool FractoriumSettings::FinalEarlyClip() { return value(FINALEARLYCLIP).toBool(); }
|
||||
void FractoriumSettings::FinalEarlyClip(bool b) { setValue(FINALEARLYCLIP, b); }
|
||||
|
||||
bool FractoriumSettings::FinalTransparency() { return value(FINALTRANSPARENCY).toBool(); }
|
||||
void FractoriumSettings::FinalTransparency(bool b) { setValue(FINALTRANSPARENCY, b); }
|
||||
|
||||
bool FractoriumSettings::FinalOpenCL() { return value(FINALOPENCL).toBool(); }
|
||||
void FractoriumSettings::FinalOpenCL(bool b) { setValue(FINALOPENCL, b); }
|
||||
|
||||
bool FractoriumSettings::FinalDouble() { return value(FINALDOUBLEPRECISION).toBool(); }
|
||||
void FractoriumSettings::FinalDouble(bool b) { setValue(FINALDOUBLEPRECISION, b); }
|
||||
|
||||
bool FractoriumSettings::FinalSaveXml() { return value(FINALSAVEXML).toBool(); }
|
||||
void FractoriumSettings::FinalSaveXml(bool b) { setValue(FINALSAVEXML, b); }
|
||||
|
||||
bool FractoriumSettings::FinalDoAll() { return value(FINALDOALL).toBool(); }
|
||||
void FractoriumSettings::FinalDoAll(bool b) { setValue(FINALDOALL, b); }
|
||||
|
||||
bool FractoriumSettings::FinalDoSequence() { return value(FINALDOSEQUENCE).toBool(); }
|
||||
void FractoriumSettings::FinalDoSequence(bool b) { setValue(FINALDOSEQUENCE, b); }
|
||||
|
||||
bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); }
|
||||
void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalScale() { return value(FINALSCALE).toUInt(); }
|
||||
void FractoriumSettings::FinalScale(unsigned int i) { setValue(FINALSCALE, i); }
|
||||
|
||||
QString FractoriumSettings::FinalDoAllExt() { return value(FINALDOALLEXT).toString(); }
|
||||
void FractoriumSettings::FinalDoAllExt(QString s) { setValue(FINALDOALLEXT, s); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalPlatformIndex() { return value(FINALPLATFORMINDEX).toUInt(); }
|
||||
void FractoriumSettings::FinalPlatformIndex(unsigned int i) { setValue(FINALPLATFORMINDEX, i); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalDeviceIndex() { return value(FINALDEVICEINDEX).toUInt(); }
|
||||
void FractoriumSettings::FinalDeviceIndex(unsigned int i) { setValue(FINALDEVICEINDEX, i); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalThreadCount() { return value(FINALTHREADCOUNT).toUInt(); }
|
||||
void FractoriumSettings::FinalThreadCount(unsigned int i) { setValue(FINALTHREADCOUNT, i); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalWidth() { return value(FINALWIDTH).toUInt(); }
|
||||
void FractoriumSettings::FinalWidth(unsigned int i) { setValue(FINALWIDTH, i); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalHeight() { return value(FINALHEIGHT).toUInt(); }
|
||||
void FractoriumSettings::FinalHeight(unsigned int i) { setValue(FINALHEIGHT, i); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalQuality() { return value(FINALQUALITY).toUInt(); }
|
||||
void FractoriumSettings::FinalQuality(unsigned int i) { setValue(FINALQUALITY, i); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalTemporalSamples() { return value(FINALTEMPORALSAMPLES).toUInt(); }
|
||||
void FractoriumSettings::FinalTemporalSamples(unsigned int i) { setValue(FINALTEMPORALSAMPLES, i); }
|
||||
|
||||
unsigned int FractoriumSettings::FinalSupersample() { return value(FINALSUPERSAMPLE).toUInt(); }
|
||||
void FractoriumSettings::FinalSupersample(unsigned int i) { setValue(FINALSUPERSAMPLE, i); }
|
||||
|
||||
/// <summary>
|
||||
/// Xml file saving settings.
|
||||
/// </summary>
|
||||
|
||||
unsigned int FractoriumSettings::XmlWidth() { return value(XMLWIDTH).toUInt(); }
|
||||
void FractoriumSettings::XmlWidth(unsigned int i) { setValue(XMLWIDTH, i); }
|
||||
|
||||
unsigned int FractoriumSettings::XmlHeight() { return value(XMLHEIGHT).toUInt(); }
|
||||
void FractoriumSettings::XmlHeight(unsigned int i) { setValue(XMLHEIGHT, i); }
|
||||
|
||||
unsigned int FractoriumSettings::XmlTemporalSamples() { return value(XMLTEMPORALSAMPLES).toUInt(); }
|
||||
void FractoriumSettings::XmlTemporalSamples(unsigned int i) { setValue(XMLTEMPORALSAMPLES, i); }
|
||||
|
||||
unsigned int FractoriumSettings::XmlQuality() { return value(XMLQUALITY).toUInt(); }
|
||||
void FractoriumSettings::XmlQuality(unsigned int i) { setValue(XMLQUALITY, i); }
|
||||
|
||||
unsigned int FractoriumSettings::XmlSupersample() { return value(XMLSUPERSAMPLE).toUInt(); }
|
||||
void FractoriumSettings::XmlSupersample(unsigned int i) { setValue(XMLSUPERSAMPLE, i); }
|
||||
|
||||
QString FractoriumSettings::Id() { return value(IDENTITYID).toString(); }
|
||||
void FractoriumSettings::Id(QString s) { setValue(IDENTITYID, s); }
|
||||
|
||||
QString FractoriumSettings::Url() { return value(IDENTITYURL).toString(); }
|
||||
void FractoriumSettings::Url(QString s) { setValue(IDENTITYURL, s); }
|
||||
|
||||
QString FractoriumSettings::Nick() { return value(IDENTITYNICK).toString(); }
|
||||
void FractoriumSettings::Nick(QString s) { setValue(IDENTITYNICK, s); }
|
||||
|
||||
/// <summary>
|
||||
/// General operations settings.
|
||||
/// </summary>
|
||||
|
||||
QString FractoriumSettings::OpenFolder() { return value(OPENFOLDER).toString(); }
|
||||
void FractoriumSettings::OpenFolder(QString s) { setValue(OPENFOLDER, s); }
|
||||
|
||||
QString FractoriumSettings::SaveFolder() { return value(SAVEFOLDER).toString(); }
|
||||
void FractoriumSettings::SaveFolder(QString s) { setValue(SAVEFOLDER, s); }
|
||||
|
||||
QString FractoriumSettings::OpenXmlExt() { return value(OPENXMLEXT).toString(); }
|
||||
void FractoriumSettings::OpenXmlExt(QString s) { setValue(OPENXMLEXT, s); }
|
||||
|
||||
QString FractoriumSettings::SaveXmlExt() { return value(SAVEXMLEXT).toString(); }
|
||||
void FractoriumSettings::SaveXmlExt(QString s) { setValue(SAVEXMLEXT, s); }
|
||||
|
||||
QString FractoriumSettings::OpenImageExt() { return value(OPENIMAGEEXT).toString(); }
|
||||
void FractoriumSettings::OpenImageExt(QString s) { setValue(OPENIMAGEEXT, s); }
|
||||
|
||||
QString FractoriumSettings::SaveImageExt() { return value(SAVEIMAGEEXT).toString(); }
|
||||
void FractoriumSettings::SaveImageExt(QString s) { setValue(SAVEIMAGEEXT, s); }
|
198
Source/Fractorium/FractoriumSettings.h
Normal file
@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumSettings class.
|
||||
/// </summary>
|
||||
|
||||
#define EARLYCLIP "render/earlyclip"
|
||||
#define TRANSPARENCY "render/transparency"
|
||||
#define OPENCL "render/opencl"
|
||||
#define DOUBLEPRECISION "render/dp64"
|
||||
#define PLATFORMINDEX "render/platformindex"
|
||||
#define DEVICEINDEX "render/deviceindex"
|
||||
#define THREADCOUNT "render/threadcount"
|
||||
#define CPUDEFILTER "render/cpudefilter"
|
||||
#define OPENCLDEFILTER "render/opencldefilter"
|
||||
#define CPUSUBBATCH "render/cpusubbatch"
|
||||
#define OPENCLSUBBATCH "render/openclsubbatch"
|
||||
|
||||
#define FINALEARLYCLIP "finalrender/earlyclip"
|
||||
#define FINALTRANSPARENCY "finalrender/transparency"
|
||||
#define FINALOPENCL "finalrender/opencl"
|
||||
#define FINALDOUBLEPRECISION "finalrender/dp64"
|
||||
#define FINALSAVEXML "finalrender/savexml"
|
||||
#define FINALDOALL "finalrender/doall"
|
||||
#define FINALDOSEQUENCE "finalrender/dosequence"
|
||||
#define FINALKEEPASPECT "finalrender/keepaspect"
|
||||
#define FINALSCALE "finalrender/scale"
|
||||
#define FINALDOALLEXT "finalrender/doallext"
|
||||
#define FINALPLATFORMINDEX "finalrender/platformindex"
|
||||
#define FINALDEVICEINDEX "finalrender/deviceindex"
|
||||
#define FINALTHREADCOUNT "finalrender/threadcount"
|
||||
#define FINALWIDTH "finalrender/width"
|
||||
#define FINALHEIGHT "finalrender/height"
|
||||
#define FINALQUALITY "finalrender/quality"
|
||||
#define FINALTEMPORALSAMPLES "finalrender/temporalsamples"
|
||||
#define FINALSUPERSAMPLE "finalrender/supersample"
|
||||
|
||||
#define XMLWIDTH "xml/width"
|
||||
#define XMLHEIGHT "xml/height"
|
||||
#define XMLTEMPORALSAMPLES "xml/temporalsamples"
|
||||
#define XMLQUALITY "xml/quality"
|
||||
#define XMLSUPERSAMPLE "xml/supersample"
|
||||
|
||||
#define OPENFOLDER "path/open"
|
||||
#define SAVEFOLDER "path/save"
|
||||
|
||||
#define OPENXMLEXT "file/openxmlext"
|
||||
#define SAVEXMLEXT "file/savexmlext"
|
||||
#define OPENIMAGEEXT "file/openimageext"
|
||||
#define SAVEIMAGEEXT "file/saveimageext"
|
||||
|
||||
#define IDENTITYID "identity/id"
|
||||
#define IDENTITYURL "identity/url"
|
||||
#define IDENTITYNICK "identity/nick"
|
||||
|
||||
/// <summary>
|
||||
/// Class for preserving various program options between
|
||||
/// runs of Fractorium. Each of these generally corresponds
|
||||
/// to items in the options dialog and the final render dialog.
|
||||
/// </summary>
|
||||
class FractoriumSettings : public QSettings
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FractoriumSettings(QObject* parent);
|
||||
void EnsureDefaults();
|
||||
|
||||
bool EarlyClip();
|
||||
void EarlyClip(bool b);
|
||||
|
||||
bool Transparency();
|
||||
void Transparency(bool b);
|
||||
|
||||
bool OpenCL();
|
||||
void OpenCL(bool b);
|
||||
|
||||
bool Double();
|
||||
void Double(bool b);
|
||||
|
||||
unsigned int PlatformIndex();
|
||||
void PlatformIndex(unsigned int b);
|
||||
|
||||
unsigned int DeviceIndex();
|
||||
void DeviceIndex(unsigned int b);
|
||||
|
||||
unsigned int ThreadCount();
|
||||
void ThreadCount(unsigned int b);
|
||||
|
||||
bool CpuDEFilter();
|
||||
void CpuDEFilter(bool b);
|
||||
|
||||
bool OpenCLDEFilter();
|
||||
void OpenCLDEFilter(bool b);
|
||||
|
||||
unsigned int CpuSubBatch();
|
||||
void CpuSubBatch(unsigned int b);
|
||||
|
||||
unsigned int OpenCLSubBatch();
|
||||
void OpenCLSubBatch(unsigned int b);
|
||||
|
||||
bool FinalEarlyClip();
|
||||
void FinalEarlyClip(bool b);
|
||||
|
||||
bool FinalTransparency();
|
||||
void FinalTransparency(bool b);
|
||||
|
||||
bool FinalOpenCL();
|
||||
void FinalOpenCL(bool b);
|
||||
|
||||
bool FinalDouble();
|
||||
void FinalDouble(bool b);
|
||||
|
||||
bool FinalSaveXml();
|
||||
void FinalSaveXml(bool b);
|
||||
|
||||
bool FinalDoAll();
|
||||
void FinalDoAll(bool b);
|
||||
|
||||
bool FinalDoSequence();
|
||||
void FinalDoSequence(bool b);
|
||||
|
||||
bool FinalKeepAspect();
|
||||
void FinalKeepAspect(bool b);
|
||||
|
||||
unsigned int FinalScale();
|
||||
void FinalScale(unsigned int i);
|
||||
|
||||
QString FinalDoAllExt();
|
||||
void FinalDoAllExt(QString s);
|
||||
|
||||
unsigned int FinalPlatformIndex();
|
||||
void FinalPlatformIndex(unsigned int b);
|
||||
|
||||
unsigned int FinalDeviceIndex();
|
||||
void FinalDeviceIndex(unsigned int b);
|
||||
|
||||
unsigned int FinalThreadCount();
|
||||
void FinalThreadCount(unsigned int b);
|
||||
|
||||
unsigned int FinalWidth();
|
||||
void FinalWidth(unsigned int i);
|
||||
|
||||
unsigned int FinalHeight();
|
||||
void FinalHeight(unsigned int i);
|
||||
|
||||
unsigned int FinalQuality();
|
||||
void FinalQuality(unsigned int i);
|
||||
|
||||
unsigned int FinalTemporalSamples();
|
||||
void FinalTemporalSamples(unsigned int i);
|
||||
|
||||
unsigned int FinalSupersample();
|
||||
void FinalSupersample(unsigned int i);
|
||||
|
||||
unsigned int XmlWidth();
|
||||
void XmlWidth(unsigned int i);
|
||||
|
||||
unsigned int XmlHeight();
|
||||
void XmlHeight(unsigned int i);
|
||||
|
||||
unsigned int XmlTemporalSamples();
|
||||
void XmlTemporalSamples(unsigned int i);
|
||||
|
||||
unsigned int XmlQuality();
|
||||
void XmlQuality(unsigned int i);
|
||||
|
||||
unsigned int XmlSupersample();
|
||||
void XmlSupersample(unsigned int i);
|
||||
|
||||
QString OpenFolder();
|
||||
void OpenFolder(QString s);
|
||||
|
||||
QString SaveFolder();
|
||||
void SaveFolder(QString s);
|
||||
|
||||
QString OpenXmlExt();
|
||||
void OpenXmlExt(QString s);
|
||||
|
||||
QString SaveXmlExt();
|
||||
void SaveXmlExt(QString s);
|
||||
|
||||
QString OpenImageExt();
|
||||
void OpenImageExt(QString s);
|
||||
|
||||
QString SaveImageExt();
|
||||
void SaveImageExt(QString s);
|
||||
|
||||
QString Id();
|
||||
void Id(QString s);
|
||||
|
||||
QString Url();
|
||||
void Url(QString s);
|
||||
|
||||
QString Nick();
|
||||
void Nick(QString s);
|
||||
};
|
21
Source/Fractorium/FractoriumToolbar.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the toolbar UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitToolbarUI()
|
||||
{
|
||||
//These aren't menus but duplicate menu functionality in a pseudo-toolbar.
|
||||
connect(ui.SaveCurrentAsXmlButton, SIGNAL(clicked(bool)), this, SLOT(OnSaveCurrentAsXmlButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.SaveEntireFileAsXmlButton, SIGNAL(clicked(bool)), this, SLOT(OnSaveEntireFileAsXmlButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.SaveCurrentToOpenedFileButton, SIGNAL(clicked(bool)), this, SLOT(OnSaveCurrentToOpenedFileButtonClicked(bool)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrappers around calls to menu items.
|
||||
/// </summary>
|
||||
|
||||
void Fractorium::OnSaveCurrentAsXmlButtonClicked(bool checked) { OnActionSaveCurrentAsXml(checked); }
|
||||
void Fractorium::OnSaveEntireFileAsXmlButtonClicked(bool checked) { OnActionSaveEntireFileAsXml(checked); }
|
||||
void Fractorium::OnSaveCurrentToOpenedFileButtonClicked(bool checked) { OnActionSaveCurrentToOpenedFile(checked); }
|
345
Source/Fractorium/FractoriumXforms.cpp
Normal file
@ -0,0 +1,345 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the xforms UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitXformsUI()
|
||||
{
|
||||
int spinHeight = 20, row = 0;
|
||||
|
||||
connect(ui.AddXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddXformButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.DuplicateXformButton, SIGNAL(clicked(bool)), this, SLOT(OnDuplicateXformButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ClearXformButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXformButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.DeleteXformButton, SIGNAL(clicked(bool)), this, SLOT(OnDeleteXformButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.AddFinalXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddFinalXformButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.CurrentXformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentXformComboChanged(int)), Qt::QueuedConnection);
|
||||
|
||||
SetFixedTableHeader(ui.XformWeightNameTable->horizontalHeader());
|
||||
//Use SetupSpinner() just to create the spinner, but use col of -1 to prevent it from being added to the table.
|
||||
SetupSpinner<DoubleSpinBox, double>(ui.XformWeightNameTable, this, row, -1, m_XformWeightSpin, spinHeight, 0, 1000, 0.05, SIGNAL(valueChanged(double)), SLOT(OnXformWeightChanged(double)), false, 0, 1, 0);
|
||||
m_XformWeightSpin->setDecimals(3);
|
||||
m_XformWeightSpin->SmallStep(0.001);
|
||||
m_XformWeightSpinnerButtonWidget = new SpinnerButtonWidget(m_XformWeightSpin, "=", 20, 19, ui.XformWeightNameTable);
|
||||
m_XformWeightSpinnerButtonWidget->m_Button->setToolTip("Equalize weights");
|
||||
m_XformWeightSpinnerButtonWidget->m_Button->setStyleSheet("text-align: center center");
|
||||
connect(m_XformWeightSpinnerButtonWidget->m_Button, SIGNAL(clicked(bool)), this, SLOT(OnEqualWeightButtonClicked(bool)), Qt::QueuedConnection);
|
||||
|
||||
ui.XformWeightNameTable->setCellWidget(0, 0, m_XformWeightSpinnerButtonWidget);
|
||||
ui.XformWeightNameTable->setItem(0, 1, new QTableWidgetItem());
|
||||
connect(ui.XformWeightNameTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnXformNameChanged(int, int)), Qt::QueuedConnection);
|
||||
|
||||
ui.CurrentXformCombo->setProperty("soloxform", -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current xform.
|
||||
/// </summary>
|
||||
/// <returns>The current xform as specified by the current xform combo box index. NULL if out of range (should never happen).</returns>
|
||||
template <typename T>
|
||||
Xform<T>* FractoriumEmberController<T>::CurrentXform()
|
||||
{
|
||||
return m_Ember.GetTotalXform(m_Fractorium->ui.CurrentXformCombo->currentIndex());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the current xform to the index passed in.
|
||||
/// </summary>
|
||||
/// <param name="i">The index to set the current xform to</param>
|
||||
void Fractorium::CurrentXform(unsigned int i)
|
||||
{
|
||||
if (i < ui.CurrentXformCombo->count())
|
||||
ui.CurrentXformCombo->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the current xform and populate all GUI widgets.
|
||||
/// Called when the current xform combo box index changes.
|
||||
/// </summary>
|
||||
/// <param name="index">The selected combo box index</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::CurrentXformComboChanged(int index)
|
||||
{
|
||||
if (Xform<T>* xform = m_Ember.GetTotalXform(index))
|
||||
{
|
||||
FillWithXform(xform);
|
||||
m_GLController->SetSelectedXform(xform);
|
||||
|
||||
int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt();
|
||||
|
||||
m_Fractorium->ui.SoloXformCheckBox->blockSignals(true);
|
||||
m_Fractorium->ui.SoloXformCheckBox->setChecked(solo == index);
|
||||
m_Fractorium->ui.SoloXformCheckBox->blockSignals(false);
|
||||
|
||||
bool enable = !IsFinal(CurrentXform());
|
||||
|
||||
m_Fractorium->ui.DuplicateXformButton->setEnabled(enable);
|
||||
m_Fractorium->m_XformWeightSpin->setEnabled(enable);
|
||||
m_Fractorium->ui.SoloXformCheckBox->setEnabled(enable);
|
||||
m_Fractorium->ui.AddFinalXformButton->setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnCurrentXformComboChanged(int index) { m_Controller->CurrentXformComboChanged(index); }
|
||||
|
||||
/// <summary>
|
||||
/// Add an empty xform in the current ember and set it as the current xform.
|
||||
/// Called when the add xform button is clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::AddXform()
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
Xform<T> newXform;
|
||||
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
||||
|
||||
newXform.m_Weight = 0.25;
|
||||
newXform.m_ColorX = m_Rand.Frand01<T>();
|
||||
m_Ember.AddXform(newXform);
|
||||
m_Fractorium->FillXforms();
|
||||
combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final.
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform(); }
|
||||
|
||||
/// <summary>
|
||||
/// Duplicate the current xform in the current ember, and set it as the current xform.
|
||||
/// Called when the duplicate xform button is clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::DuplicateXform()
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
||||
|
||||
if (xform && !IsFinal(xform))
|
||||
{
|
||||
m_Ember.AddXform(*xform);
|
||||
m_Fractorium->FillXforms();
|
||||
combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnDuplicateXformButtonClicked(bool checked) { m_Controller->DuplicateXform(); }
|
||||
|
||||
/// <summary>
|
||||
/// Clear all variations from the current xform, affine, palette and xaos are left untouched.
|
||||
/// Called when the clear xform button is clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ClearCurrentXform()
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
xform->ClearAndDeleteVariations();
|
||||
//Note xaos is left alone.
|
||||
FillVariationTreeWithXform(xform);
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearCurrentXform(); }
|
||||
|
||||
/// <summary>
|
||||
/// Delete the current xform.
|
||||
/// Will not delete the last remaining non-final xform.
|
||||
/// Called when the delete xform button is clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::DeleteCurrentXform()
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
bool haveFinal = m_Fractorium->HaveFinal();
|
||||
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
||||
int count = combo->count();
|
||||
int index = combo->currentIndex();
|
||||
|
||||
//Do not allow deleting the only remaining non-final xform.
|
||||
if (haveFinal && count <= 2 && index == 0)
|
||||
return;
|
||||
|
||||
if (!haveFinal && count == 1)
|
||||
return;
|
||||
|
||||
m_Ember.DeleteTotalXform(index);
|
||||
m_Fractorium->FillXforms();
|
||||
combo->setCurrentIndex(combo->count() - (haveFinal ? 2 : 1));//Set index to the last item before final.
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->DeleteCurrentXform(); }
|
||||
|
||||
/// <summary>
|
||||
/// Add a final xform to the ember and set it as the current xform.
|
||||
/// Will only take action if a final xform is not already present.
|
||||
/// Called when the add final xform button is clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::AddFinalXform()
|
||||
{
|
||||
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
||||
|
||||
//Check to see if a final xform is already present.
|
||||
if (!m_Fractorium->HaveFinal())
|
||||
{
|
||||
Xform<T> xform;
|
||||
|
||||
xform.AddVariation(new LinearVariation<T>());//Just a placeholder so other parts of the code don't see it as being empty.
|
||||
m_Ember.SetFinalXform(xform);
|
||||
combo->addItem("Final");
|
||||
combo->setCurrentIndex(combo->count() - 1);//Set index to the last item.
|
||||
UpdateRender();
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddFinalXform(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the weight of the current xform.
|
||||
/// Called when weight spinner changes.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The weight</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XformWeightChanged(double d)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
xform->m_Weight = d;
|
||||
SetNormalizedWeightText(xform);
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnXformWeightChanged(double d) { m_Controller->XformWeightChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Equalize the weights of all xforms in the ember.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::EqualizeWeights()
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
m_Ember.EqualizeWeights();
|
||||
m_Fractorium->m_XformWeightSpin->setValue(xform->m_Weight);//Will trigger an update, so pass false to updateRender below.
|
||||
}, false);
|
||||
}
|
||||
|
||||
void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->EqualizeWeights(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the name of the current xform.
|
||||
/// Called when the user types in the name cell of the table.
|
||||
/// </summary>
|
||||
/// <param name="row">The row of the cell</param>
|
||||
/// <param name="col">The col of the cell</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XformNameChanged(int row, int col)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
int index = m_Ember.GetXformIndex(xform);
|
||||
|
||||
xform->m_Name = m_Fractorium->ui.XformWeightNameTable->item(row, col)->text().toStdString();
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
if (QTableWidgetItem* xformNameItem = m_Fractorium->ui.XaosTable->item(index, 0))
|
||||
xformNameItem->setText(MakeXaosNameString(index));
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameChanged(row, col); }
|
||||
|
||||
/// <summary>
|
||||
/// Fill all GUI widgets with values from the passed in xform.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform whose values will be used to populate the widgets</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FillWithXform(Xform<T>* xform)
|
||||
{
|
||||
m_Fractorium->m_XformWeightSpin->SetValueStealth(xform->m_Weight);
|
||||
SetNormalizedWeightText(xform);
|
||||
|
||||
if (QTableWidgetItem* item = m_Fractorium->ui.XformWeightNameTable->item(0, 1))
|
||||
{
|
||||
m_Fractorium->ui.XformWeightNameTable->blockSignals(true);
|
||||
item->setText(QString::fromStdString(xform->m_Name));
|
||||
m_Fractorium->ui.XformWeightNameTable->blockSignals(false);
|
||||
}
|
||||
|
||||
FillVariationTreeWithXform(xform);
|
||||
FillColorWithXform(xform);
|
||||
FillAffineWithXform(xform, true);
|
||||
FillAffineWithXform(xform, false);
|
||||
FillXaosWithCurrentXform();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the normalized weight of the current xform as the suffix text of the weight spinner.
|
||||
/// </summary>
|
||||
/// <param name="xform">The current xform whose normalized weight will be shown</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SetNormalizedWeightText(Xform<T>* xform)
|
||||
{
|
||||
if (xform)
|
||||
{
|
||||
int index = m_Ember.GetXformIndex(xform);
|
||||
|
||||
m_Ember.CalcNormalizedWeights(m_NormalizedWeights);
|
||||
|
||||
if (index != -1 && index < m_NormalizedWeights.size())
|
||||
m_Fractorium->m_XformWeightSpin->setSuffix(QString(" (") + QString::number((double)m_NormalizedWeights[index], 'g', 3) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether the specified xform is the final xform in the ember.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform to examine</param>
|
||||
/// <returns>True if final, else false.</returns>
|
||||
template <typename T>
|
||||
bool FractoriumEmberController<T>::IsFinal(Xform<T>* xform)
|
||||
{
|
||||
return (m_Fractorium->HaveFinal() && (xform == m_Ember.FinalXform()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the xforms combo box with the xforms in the current ember.
|
||||
/// Select the first one and fill all widgets with its values.
|
||||
/// </summary>
|
||||
void Fractorium::FillXforms()
|
||||
{
|
||||
int spinHeight = 20;
|
||||
QComboBox* combo = ui.CurrentXformCombo;
|
||||
|
||||
combo->blockSignals(true);
|
||||
combo->clear();
|
||||
|
||||
for (int i = 0; i < m_Controller->XformCount(); i++)
|
||||
combo->addItem(QString::number(i + 1));
|
||||
|
||||
if (m_Controller->UseFinalXform())
|
||||
combo->addItem("Final");
|
||||
|
||||
combo->blockSignals(false);
|
||||
combo->setCurrentIndex(0);
|
||||
FillXaosTable();
|
||||
OnSoloXformCheckBoxStateChanged(Qt::Unchecked);
|
||||
OnCurrentXformComboChanged(0);//Make sure the event gets called, because it won't if the zero index is already selected.
|
||||
}
|
492
Source/Fractorium/FractoriumXformsAffine.cpp
Normal file
@ -0,0 +1,492 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the xforms affine UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitXformsAffineUI()
|
||||
{
|
||||
int row = 0, affinePrec = 6, spinHeight = 20;
|
||||
double affineStep = 0.01, affineMin = std::numeric_limits<double>::lowest(), affineMax = std::numeric_limits<double>::max();
|
||||
QTableWidget* table = ui.PreAffineTable;
|
||||
|
||||
SetFixedTableHeader(table->horizontalHeader(), QHeaderView::Stretch);//The designer continually clobbers these values, so must manually set them here.
|
||||
SetFixedTableHeader(table->verticalHeader());
|
||||
|
||||
//Pre affine spinners.
|
||||
SetupAffineSpinner(table, this, 0, 0, m_PreX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double)));
|
||||
SetupAffineSpinner(table, this, 0, 1, m_PreX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double)));
|
||||
SetupAffineSpinner(table, this, 1, 0, m_PreY1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY1Changed(double)));
|
||||
SetupAffineSpinner(table, this, 1, 1, m_PreY2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY2Changed(double)));
|
||||
SetupAffineSpinner(table, this, 2, 0, m_PreO1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO1Changed(double)));
|
||||
SetupAffineSpinner(table, this, 2, 1, m_PreO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double)));
|
||||
|
||||
table = ui.PostAffineTable;
|
||||
SetFixedTableHeader(table->horizontalHeader(), QHeaderView::Stretch);//The designer continually clobbers these values, so must manually set them here.
|
||||
SetFixedTableHeader(table->verticalHeader());
|
||||
|
||||
//Post affine spinners.
|
||||
SetupAffineSpinner(table, this, 0, 0, m_PostX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double)));
|
||||
SetupAffineSpinner(table, this, 0, 1, m_PostX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double)));
|
||||
SetupAffineSpinner(table, this, 1, 0, m_PostY1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY1Changed(double)));
|
||||
SetupAffineSpinner(table, this, 1, 1, m_PostY2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY2Changed(double)));
|
||||
SetupAffineSpinner(table, this, 2, 0, m_PostO1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO1Changed(double)));
|
||||
SetupAffineSpinner(table, this, 2, 1, m_PostO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double)));
|
||||
|
||||
ui.PreRotateCombo->setValidator(new QDoubleValidator(ui.PreRotateCombo));
|
||||
ui.PreMoveCombo->setValidator( new QDoubleValidator(ui.PreMoveCombo));
|
||||
ui.PreScaleCombo->setValidator( new QDoubleValidator(ui.PreScaleCombo));
|
||||
|
||||
ui.PostRotateCombo->setValidator(new QDoubleValidator(ui.PostRotateCombo));
|
||||
ui.PostMoveCombo->setValidator( new QDoubleValidator(ui.PostMoveCombo));
|
||||
ui.PostScaleCombo->setValidator( new QDoubleValidator(ui.PostScaleCombo));
|
||||
|
||||
connect(ui.PreFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
|
||||
connect(ui.PostFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
|
||||
connect(ui.PreAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
|
||||
|
||||
connect(ui.ShowPreAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPreAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPostAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPostAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
|
||||
ui.PostAffineGroupBox->setChecked(true);//Flip it once to force the disabling of the group box.
|
||||
ui.PostAffineGroupBox->setChecked(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for setting the value of a single pre/post affine coefficient.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The value to set</param>
|
||||
/// <param name="index">The index to set, 0-5, corresponding to a, b, c, d, e, f</param>
|
||||
/// <param name="pre">True if pre affine, false if post affine.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::AffineSetHelper(double d, int index, bool pre)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
QObject* objSender = m_Fractorium->sender();
|
||||
DoubleSpinBox* spinBox = dynamic_cast<DoubleSpinBox*>(objSender);
|
||||
|
||||
if (spinBox)
|
||||
{
|
||||
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
affine->A(spinBox->value());
|
||||
break;
|
||||
case 1:
|
||||
affine->B(spinBox->value());
|
||||
break;
|
||||
case 2:
|
||||
affine->C(spinBox->value());
|
||||
break;
|
||||
case 3:
|
||||
affine->D(spinBox->value());
|
||||
break;
|
||||
case 4:
|
||||
affine->E(spinBox->value());
|
||||
break;
|
||||
case 5:
|
||||
affine->F(spinBox->value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre and post affine spinner changed events.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
void Fractorium::OnX1Changed(double d) { m_Controller->AffineSetHelper(d, 0, sender() == m_PreX1Spin); }
|
||||
void Fractorium::OnX2Changed(double d) { m_Controller->AffineSetHelper(d, 3, sender() == m_PreX2Spin); }
|
||||
void Fractorium::OnY1Changed(double d) { m_Controller->AffineSetHelper(d, 1, sender() == m_PreY1Spin); }
|
||||
void Fractorium::OnY2Changed(double d) { m_Controller->AffineSetHelper(d, 4, sender() == m_PreY2Spin); }
|
||||
void Fractorium::OnO1Changed(double d) { m_Controller->AffineSetHelper(d, 2, sender() == m_PreO1Spin); }
|
||||
void Fractorium::OnO2Changed(double d) { m_Controller->AffineSetHelper(d, 5, sender() == m_PreO2Spin); }
|
||||
|
||||
/// <summary>
|
||||
/// Flip the current pre/post affine vertically and/or horizontally.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="horizontal">True to flip horizontally</param>
|
||||
/// <param name="vertical">True to flip vertically</param>
|
||||
/// <param name="pre">True if pre affine, else post affine.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FlipCurrentXform(bool horizontal, bool vertical, bool pre)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
|
||||
|
||||
if (horizontal)
|
||||
{
|
||||
affine->A(-affine->A());
|
||||
affine->B(-affine->B());
|
||||
|
||||
if (!m_Fractorium->LocalPivot())
|
||||
affine->C(-affine->C());
|
||||
}
|
||||
|
||||
if (vertical)
|
||||
{
|
||||
affine->D(-affine->D());
|
||||
affine->E(-affine->E());
|
||||
|
||||
if (!m_Fractorium->LocalPivot())
|
||||
affine->F(-affine->F());
|
||||
}
|
||||
|
||||
FillAffineWithXform(xform, pre);
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnFlipHorizontalButtonClicked(bool checked) { m_Controller->FlipCurrentXform(true, false, sender() == ui.PreFlipHorizontalButton); }
|
||||
void Fractorium::OnFlipVerticalButtonClicked(bool checked) { m_Controller->FlipCurrentXform(false, true, sender() == ui.PreFlipVerticalButton); }
|
||||
|
||||
/// <summary>
|
||||
/// Rotate the current pre/post affine transform x degrees.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle to rotate by</param>
|
||||
/// <param name="pre">True if pre affine, else post affine.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::RotateCurrentXformByAngle(double angle, bool pre)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
|
||||
|
||||
affine->Rotate(angle);
|
||||
FillAffineWithXform(xform, pre);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotate the selected pre/post affine transform 90 degrees clockwise/counter clockwise.
|
||||
/// Called when the rotate 90 c/cc pre/post buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnRotate90CButtonClicked(bool checked) { m_Controller->RotateCurrentXformByAngle(90, sender() == ui.PreRotate90CButton); }
|
||||
void Fractorium::OnRotate90CcButtonClicked(bool checked) { m_Controller->RotateCurrentXformByAngle(-90, sender() == ui.PreRotate90CcButton); }
|
||||
|
||||
/// <summary>
|
||||
/// Rotate the selected pre/post affine transform x degrees clockwise.
|
||||
/// Called when the rotate x c pre/post buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnRotateCButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreRotateCButton;
|
||||
QComboBox* combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->RotateCurrentXformByAngle(d, pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotate the selected pre/post affine transform x degrees counter clockwise.
|
||||
/// Called when the rotate x cc pre/post buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnRotateCcButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreRotateCcButton;
|
||||
QComboBox* combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->RotateCurrentXformByAngle(-d, pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the current pre/post affine in the x and y directions by the specified amount.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="x">The x direction to move</param>
|
||||
/// <param name="y">The y direction to move</param>
|
||||
/// <param name="pre">True if pre affine, else post affine.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::MoveCurrentXform(double x, double y, bool pre)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
|
||||
|
||||
affine->C(affine->C() + x);
|
||||
affine->F(affine->F() + y);
|
||||
FillAffineWithXform(xform, pre);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the selected pre/post affine transform x units up.
|
||||
/// Called when the move pre/post up buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnMoveUpButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreMoveUpButton;
|
||||
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->MoveCurrentXform(0, d, pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the selected pre/post affine transform x units down.
|
||||
/// Called when the move pre/post down buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnMoveDownButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreMoveDownButton;
|
||||
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->MoveCurrentXform(0, -d, pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the selected pre/post affine transform x units left.
|
||||
/// Called when the move pre/post left buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnMoveLeftButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreMoveLeftButton;
|
||||
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->MoveCurrentXform(-d, 0, pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the selected pre/post affine transform x units right.
|
||||
/// Called when the move pre/post right buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnMoveRightButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreMoveRightButton;
|
||||
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->MoveCurrentXform(d, 0, pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scale the current pre/post affine by the specified amount.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale value</param>
|
||||
/// <param name="pre">True if pre affine, else post affine.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ScaleCurrentXform(double scale, bool pre)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
|
||||
|
||||
affine->A(affine->A() * scale);
|
||||
affine->B(affine->B() * scale);
|
||||
affine->D(affine->D() * scale);
|
||||
affine->E(affine->E() * scale);
|
||||
FillAffineWithXform(xform, pre);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scale the selected pre/post affine transform x units down.
|
||||
/// Called when the scale pre/post down buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnScaleDownButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreScaleDownButton;
|
||||
QComboBox* combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->ScaleCurrentXform(1.0 / (d / 100.0), pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scale the selected pre/post affine transform x units up.
|
||||
/// Called when the scale pre/post up buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnScaleUpButtonClicked(bool checked)
|
||||
{
|
||||
bool ok;
|
||||
bool pre = sender() == ui.PreScaleUpButton;
|
||||
QComboBox* combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo;
|
||||
double d = combo->currentText().toDouble(&ok);
|
||||
|
||||
if (ok)
|
||||
m_Controller->ScaleCurrentXform(d / 100.0, pre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset pre/post affine to the identity matrix.
|
||||
/// Called when reset pre/post affine buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ResetCurrentXformAffine(bool pre)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
|
||||
|
||||
affine->MakeID();
|
||||
FillAffineWithXform(xform, pre);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset pre/post affine to the identity matrix.
|
||||
/// Called when reset pre/post affine buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
void Fractorium::OnResetAffineButtonClicked(bool checked) { m_Controller->ResetCurrentXformAffine(sender() == ui.PreResetButton); }
|
||||
|
||||
/// <summary>
|
||||
/// Fill the GUI with the pre/post affine xform values.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform to fill with</param>
|
||||
/// <param name="pre">True if pre affine, else post affine.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FillAffineWithXform(Xform<T>* xform, bool pre)
|
||||
{
|
||||
if (pre)
|
||||
{
|
||||
m_Fractorium->m_PreX1Spin->SetValueStealth(xform->m_Affine.A());
|
||||
m_Fractorium->m_PreY1Spin->SetValueStealth(xform->m_Affine.B());
|
||||
m_Fractorium->m_PreO1Spin->SetValueStealth(xform->m_Affine.C());
|
||||
m_Fractorium->m_PreX2Spin->SetValueStealth(xform->m_Affine.D());
|
||||
m_Fractorium->m_PreY2Spin->SetValueStealth(xform->m_Affine.E());
|
||||
m_Fractorium->m_PreO2Spin->SetValueStealth(xform->m_Affine.F());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Fractorium->m_PostX1Spin->SetValueStealth(xform->m_Post.A());
|
||||
m_Fractorium->m_PostY1Spin->SetValueStealth(xform->m_Post.B());
|
||||
m_Fractorium->m_PostO1Spin->SetValueStealth(xform->m_Post.C());
|
||||
m_Fractorium->m_PostX2Spin->SetValueStealth(xform->m_Post.D());
|
||||
m_Fractorium->m_PostY2Spin->SetValueStealth(xform->m_Post.E());
|
||||
m_Fractorium->m_PostO2Spin->SetValueStealth(xform->m_Post.F());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger a redraw which will show or hide the circle affine transforms
|
||||
/// based on whether each group box is checked or not.
|
||||
/// Called when the group box check box for pre or post affine is checked.
|
||||
/// </summary>
|
||||
/// <param name="on">Ignored</param>
|
||||
void Fractorium::OnAffineGroupBoxToggled(bool on)
|
||||
{
|
||||
ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger a redraw which will show all, or only the current, circle affine transforms
|
||||
/// based on which radio buttons are selected.
|
||||
/// Called when and pre/post show all/current radio buttons are checked.
|
||||
/// </summary>
|
||||
/// <param name="on">Ignored</param>
|
||||
void Fractorium::OnAffineDrawAllCurrentRadioButtonToggled(bool checked)
|
||||
{
|
||||
OnCurrentXformComboChanged(ui.CurrentXformCombo->currentIndex());
|
||||
ui.GLDisplay->update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup a spinner to be placed in a table cell.
|
||||
/// Special setup function for affine spinners which differs slightly from the regular
|
||||
/// SetupSpinner() function.
|
||||
/// </summary>
|
||||
/// <param name="table">The table the spinner belongs to</param>
|
||||
/// <param name="receiver">The receiver object</param>
|
||||
/// <param name="row">The row in the table where this spinner resides</param>
|
||||
/// <param name="col">The col in the table where this spinner resides</param>
|
||||
/// <param name="spinBox">Double pointer to spin box which will hold the spinner upon exit</param>
|
||||
/// <param name="height">The height of the spinner</param>
|
||||
/// <param name="min">The minimum value of the spinner</param>
|
||||
/// <param name="max">The maximum value of the spinner</param>
|
||||
/// <param name="step">The step of the spinner</param>
|
||||
/// <param name="prec">The precision of the spinner</param>
|
||||
/// <param name="signal">The signal the spinner emits</param>
|
||||
/// <param name="slot">The slot to receive the signal</param>
|
||||
void Fractorium::SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, DoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot)
|
||||
{
|
||||
spinBox = new DoubleSpinBox(table, height, step);
|
||||
spinBox->setRange(min, max);
|
||||
spinBox->setDecimals(prec);
|
||||
table->setCellWidget(row, col, spinBox);
|
||||
connect(spinBox, signal, receiver, slot, Qt::QueuedConnection);
|
||||
spinBox->DoubleClick(true);
|
||||
spinBox->DoubleClickNonZero(0);
|
||||
spinBox->DoubleClickZero(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GUI wrapper functions, getters only.
|
||||
/// </summary>
|
||||
|
||||
bool Fractorium::DrawAllPre() { return ui.ShowPreAffineAllRadio->isChecked(); }
|
||||
bool Fractorium::DrawAllPost() { return ui.ShowPostAffineAllRadio->isChecked(); }
|
||||
bool Fractorium::LocalPivot() { return ui.LocalPivotRadio->isChecked(); }
|
204
Source/Fractorium/FractoriumXformsColor.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the xforms color UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitXformsColorUI()
|
||||
{
|
||||
int spinHeight = 20, row = 0;
|
||||
|
||||
m_XformColorValueItem = new QTableWidgetItem();
|
||||
ui.XformColorIndexTable->setItem(0, 0, m_XformColorValueItem);
|
||||
|
||||
m_PaletteRefItem = new QTableWidgetItem();
|
||||
ui.XformPaletteRefTable->setItem(0, 0, m_PaletteRefItem);
|
||||
ui.XformPaletteRefTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
connect(ui.XformPaletteRefTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), this, SLOT(OnXformRefPaletteResized(int, int, int)), 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);
|
||||
SetupSpinner<DoubleSpinBox, double>(ui.XformColorValuesTable, this, row, 1, m_XformDirectColorSpin, spinHeight, 0, 1, 0.1, SIGNAL(valueChanged(double)), SLOT(OnXformDirectColorChanged(double)), true, 1, 1, 0);
|
||||
|
||||
m_XformColorIndexSpin->setDecimals(3);
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the color index of the current xform.
|
||||
/// Update the color index scrollbar to match.
|
||||
/// Called when spinner in the color index cell in the palette ref table is changed.
|
||||
/// Optionally resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The color index, 0-1/</param>
|
||||
/// <param name="updateRender">True to reset the rendering process, else don't.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XformColorIndexChanged(double d, bool updateRender)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
QScrollBar* scroll = m_Fractorium->ui.XformColorScroll;
|
||||
int scrollVal = d * scroll->maximum();
|
||||
|
||||
scroll->blockSignals(true);
|
||||
scroll->setValue(scrollVal);
|
||||
scroll->blockSignals(false);
|
||||
|
||||
SetCurrentXformColorIndex(d);
|
||||
}, updateRender);
|
||||
}
|
||||
|
||||
void Fractorium::OnXformColorIndexChanged(double d) { OnXformColorIndexChanged(d, true); }
|
||||
void Fractorium::OnXformColorIndexChanged(double d, bool updateRender) { m_Controller->XformColorIndexChanged(d, updateRender); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the color index of the current xform.
|
||||
/// Update the color index cell in the palette ref table to match.
|
||||
/// Called when color index scrollbar is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The color index, 0-1.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XformScrollColorIndexChanged(int d)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
m_Fractorium->m_XformColorIndexSpin->setValue(d / (double)m_Fractorium->ui.XformColorScroll->maximum());//Will trigger an update.
|
||||
}, false);
|
||||
}
|
||||
|
||||
void Fractorium::OnXformScrollColorIndexChanged(int d) { m_Controller->XformScrollColorIndexChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the color speed of the current xform.
|
||||
/// Called when xform color speed spinner is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The color speed, -1-1.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XformColorSpeedChanged(double d) { UpdateCurrentXform([&] (Xform<T>* xform) { xform->m_ColorSpeed = d; }); }
|
||||
void Fractorium::OnXformColorSpeedChanged(double d) { m_Controller->XformColorSpeedChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the opacity of the current xform.
|
||||
/// Called when xform opacity spinner is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The opacity, 0-1.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XformOpacityChanged(double d) { UpdateCurrentXform([&] (Xform<T>* xform) { xform->m_Opacity = d; }); }
|
||||
void Fractorium::OnXformOpacityChanged(double d) { m_Controller->XformOpacityChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set the direct color percentage of the current xform.
|
||||
/// Called when xform direct color spinner is changed.
|
||||
/// Note this only affects xforms that include a dc_ variation.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The direct color percentage, 0-1.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XformDirectColorChanged(double d) { UpdateCurrentXform([&] (Xform<T>* xform) { xform->m_DirectColor = d; }); }
|
||||
void Fractorium::OnXformDirectColorChanged(double d) { m_Controller->XformDirectColorChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Set whether the current xform should be rendered solo.
|
||||
/// If checked, current is solo, if unchecked, none are solo.
|
||||
/// Solo means that all other xforms will have their opacity temporarily
|
||||
/// set to zero while rendering so that only the effect of current xform is visible.
|
||||
/// This will not permanently alter the ember, as the temporary opacity values will be applied
|
||||
/// right before rendering and reset right after.
|
||||
/// Called when solo xform check box is checked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="state">The state of the checkbox</param>
|
||||
void Fractorium::OnSoloXformCheckBoxStateChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked)
|
||||
{
|
||||
ui.CurrentXformCombo->setProperty("soloxform", ui.CurrentXformCombo->currentIndex());
|
||||
ui.SoloXformCheckBox->setText("Solo (" + QString::number(ui.CurrentXformCombo->currentIndex() + 1) + ")");
|
||||
}
|
||||
else if (state == Qt::Unchecked)
|
||||
{
|
||||
ui.CurrentXformCombo->setProperty("soloxform", -1);
|
||||
ui.SoloXformCheckBox->setText("Solo");
|
||||
}
|
||||
|
||||
m_Controller->UpdateRender();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redraw the palette ref table.
|
||||
/// Called on resize.
|
||||
/// </summary>
|
||||
/// <param name="logicalIndex">Ignored</param>
|
||||
/// <param name="oldSize">Ignored</param>
|
||||
/// <param name="newSize">Ignored</param>
|
||||
void Fractorium::OnXformRefPaletteResized(int logicalIndex, int oldSize, int newSize)
|
||||
{
|
||||
m_Controller->SetPaletteRefTable(NULL);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the current xform color index spinner to the current xform's color index.
|
||||
/// Set the color cell in the palette ref table.
|
||||
/// </summary>
|
||||
/// <param name="d">The index value to set, 0-1.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SetCurrentXformColorIndex(double d)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
xform->m_ColorX = Clamp<T>(d, 0, 1);
|
||||
|
||||
//Grab the current color from the index and assign it to the first cell of the first table.
|
||||
v4T entry = m_Ember.m_Palette[Clamp<int>(d * COLORMAP_LENGTH_MINUS_1, 0, m_Ember.m_Palette.Size())];
|
||||
|
||||
entry.r *= 255;
|
||||
entry.g *= 255;
|
||||
entry.b *= 255;
|
||||
|
||||
QRgb rgb = (unsigned int)entry.r << 16 | (unsigned int)entry.g << 8 | (unsigned int)entry.b;
|
||||
m_Fractorium->ui.XformColorIndexTable->item(0, 0)->setBackgroundColor(QColor::fromRgb(rgb));
|
||||
}, false);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform whose values will be copied to the GUI</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FillColorWithXform(Xform<T>* xform)
|
||||
{
|
||||
m_Fractorium->m_XformColorIndexSpin->SetValueStealth(xform->m_ColorX);
|
||||
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(xform->m_ColorSpeed);
|
||||
m_Fractorium->m_XformOpacitySpin->SetValueStealth(xform->m_Opacity);
|
||||
m_Fractorium->m_XformDirectColorSpin->SetValueStealth(xform->m_DirectColor);
|
||||
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.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the palette reference table to the passed in pixmap
|
||||
/// </summary>
|
||||
/// <param name="pixmap">The pixmap</param>
|
||||
void FractoriumEmberControllerBase::SetPaletteRefTable(QPixmap* pixmap)
|
||||
{
|
||||
QSize size(m_Fractorium->ui.XformPaletteRefTable->columnWidth(0), m_Fractorium->ui.XformPaletteRefTable->rowHeight(0) + 1);
|
||||
|
||||
if (pixmap)
|
||||
{
|
||||
m_Fractorium->m_PaletteRefItem->setData(Qt::DecorationRole, pixmap->scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
else if (!m_FinalPaletteImage.isNull())
|
||||
{
|
||||
QPixmap pixTemp = QPixmap::fromImage(m_FinalPaletteImage);
|
||||
|
||||
m_Fractorium->m_PaletteRefItem->setData(Qt::DecorationRole, pixTemp.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
}
|
316
Source/Fractorium/FractoriumXformsVariations.cpp
Normal file
@ -0,0 +1,316 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the xforms variations UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitXformsVariationsUI()
|
||||
{
|
||||
QTreeWidget* tree = ui.VariationsTree;
|
||||
|
||||
tree->clear();
|
||||
tree->header()->setSectionsClickable(true);
|
||||
connect(tree->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnTreeHeaderSectionClicked(int)));
|
||||
connect(ui.VariationsFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnVariationsFilterLineEditTextChanged(const QString&)));
|
||||
connect(ui.VariationsFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnVariationsFilterLineEditTextChanged(const QString&)));
|
||||
connect(ui.VariationsFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnVariationsFilterClearButtonClicked(bool)));
|
||||
|
||||
//Setting dimensions in the designer with a layout is futile, so must hard code here.
|
||||
tree->setColumnWidth(0, 160);
|
||||
tree->setColumnWidth(1, 23);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dynamically populate the variation tree widget with VariationTreeWidgetItem and VariationTreeDoubleSpinBox
|
||||
/// templated with the correct type.
|
||||
/// This will clear any previous contents.
|
||||
/// Called upon initialization, or controller type change.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::SetupVariationTree()
|
||||
{
|
||||
T fMin = TLOW;
|
||||
T fMax = TMAX;
|
||||
QSize hint0(75, 16);
|
||||
QSize hint1(30, 16);
|
||||
QTreeWidget* tree = m_Fractorium->ui.VariationsTree;
|
||||
|
||||
tree->clear();
|
||||
tree->blockSignals(true);
|
||||
|
||||
for (size_t i = 0; i < m_VariationList.Size(); i++)
|
||||
{
|
||||
Variation<T>* var = m_VariationList.GetVariation(i);
|
||||
ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(var);
|
||||
|
||||
//First add the variation, with a spinner for its weight.
|
||||
VariationTreeWidgetItem<T>* item = new VariationTreeWidgetItem<T>(tree);
|
||||
VariationTreeDoubleSpinBox<T>* spinBox = new VariationTreeDoubleSpinBox<T>(tree, parVar ? parVar : var, "");
|
||||
|
||||
item->setText(0, QString::fromStdString(var->Name()));
|
||||
item->setSizeHint(0, hint0);
|
||||
item->setSizeHint(1, hint1);
|
||||
spinBox->setRange(fMin, fMax);
|
||||
spinBox->DoubleClick(true);
|
||||
spinBox->DoubleClickZero(1);
|
||||
spinBox->DoubleClickNonZero(0);
|
||||
spinBox->SmallStep(0.001);
|
||||
tree->setItemWidget(item, 1, spinBox);
|
||||
m_Fractorium->connect(spinBox, SIGNAL(valueChanged(double)), SLOT(OnVariationSpinBoxValueChanged(double)), Qt::QueuedConnection);
|
||||
|
||||
//Check to see if the variation was parametric, and add a tree entry with a spinner for each parameter.
|
||||
if (parVar)
|
||||
{
|
||||
ParamWithName<T>* params = parVar->Params();
|
||||
|
||||
for (size_t j = 0; j< parVar->ParamCount(); j++)
|
||||
{
|
||||
if (!params[j].IsPrecalc())
|
||||
{
|
||||
VariationTreeWidgetItem<T>* paramWidget = new VariationTreeWidgetItem<T>(item);
|
||||
VariationTreeDoubleSpinBox<T>* varSpinBox = new VariationTreeDoubleSpinBox<T>(tree, parVar, params[j].Name());
|
||||
|
||||
paramWidget->setText(0, params[j].Name().c_str());
|
||||
paramWidget->setSizeHint(0, hint0);
|
||||
paramWidget->setSizeHint(1, hint1);
|
||||
varSpinBox->setRange(params[j].Min(), params[j].Max());
|
||||
varSpinBox->setValue(params[j].ParamVal());
|
||||
varSpinBox->DoubleClick(true);
|
||||
varSpinBox->DoubleClickZero(1);
|
||||
varSpinBox->DoubleClickNonZero(0);
|
||||
|
||||
if (params[j].Type() == INTEGER || params[j].Type() == INTEGER_NONZERO)
|
||||
{
|
||||
varSpinBox->setSingleStep(1);
|
||||
varSpinBox->Step(1);
|
||||
varSpinBox->SmallStep(1);
|
||||
}
|
||||
|
||||
tree->setItemWidget(paramWidget, 1, varSpinBox);
|
||||
m_Fractorium->connect(varSpinBox, SIGNAL(valueChanged(double)), SLOT(OnVariationSpinBoxValueChanged(double)), Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tree->blockSignals(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set every spinner in the variation tree, including params, to zero.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ClearVariationsTree()
|
||||
{
|
||||
QTreeWidget* tree = m_Fractorium->ui.VariationsTree;
|
||||
|
||||
for (unsigned int i = 0; i < tree->topLevelItemCount(); i++)
|
||||
{
|
||||
QTreeWidgetItem* item = tree->topLevelItem(i);
|
||||
VariationTreeDoubleSpinBox<T>* spinBox = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(tree->itemWidget(item, 1));
|
||||
|
||||
spinBox->SetValueStealth(0);
|
||||
|
||||
for (unsigned int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params.
|
||||
{
|
||||
if (spinBox = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(tree->itemWidget(item->child(j), 1)))//Cast the child widget to the VariationTreeDoubleSpinBox type.
|
||||
spinBox->SetValueStealth(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the value of a variation or param spinner to its corresponding value
|
||||
/// in the currently selected xform.
|
||||
/// Called when any spinner in the variations tree is changed.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="d">The spinner value</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::VariationSpinBoxValueChanged(double d)
|
||||
{
|
||||
QObject* objSender = m_Fractorium->sender();
|
||||
QTreeWidget* tree = m_Fractorium->ui.VariationsTree;
|
||||
VariationTreeDoubleSpinBox<T>* sender = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(objSender);
|
||||
Xform<T>* xform = m_Ember.GetTotalXform(m_Fractorium->ui.CurrentXformCombo->currentIndex());//Will retrieve normal xform or final if needed.
|
||||
|
||||
if (sender && xform)
|
||||
{
|
||||
Variation<T>* var = sender->GetVariation();//The variation attached to the sender, for reference only.
|
||||
ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(var);//The parametric cast of that variation.
|
||||
Variation<T>* xformVar = xform->GetVariationByName(var->Name());//The corresponding variation in the currently selected xform.
|
||||
QList<QTreeWidgetItem*> items = tree->findItems(QString::fromStdString(var->Name()), Qt::MatchExactly);
|
||||
bool isParam = parVar && sender->IsParam();
|
||||
|
||||
if (isParam)
|
||||
{
|
||||
//Do not take action if the xform doesn't contain the variation which this param is part of.
|
||||
if (ParametricVariation<T>* xformParVar = dynamic_cast<ParametricVariation<T>*>(xformVar))//The parametric cast of the xform's variation.
|
||||
{
|
||||
if (xformParVar->SetParamVal(sender->ParamName().c_str(), d))
|
||||
{
|
||||
UpdateRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//If they spun down to zero, and it wasn't a parameter item,
|
||||
//and the current xform contained the variation, then remove the variation.
|
||||
if (IsNearZero(d))
|
||||
{
|
||||
if (xformVar)
|
||||
xform->DeleteVariationById(var->VariationId());
|
||||
|
||||
items[0]->setBackgroundColor(0, QColor(255, 255, 255));//Ensure background is always white if weight goes to zero.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xformVar)//The xform already contained this variation, which means they just went from a non-zero weight to another non-zero weight (the simple case).
|
||||
{
|
||||
xformVar->m_Weight = d;
|
||||
}
|
||||
else
|
||||
{
|
||||
//If the item wasn't a param and the xform did not contain this variation,
|
||||
//it means they went from zero to a non-zero weight, so add a new copy of this xform.
|
||||
Variation<T>* newVar = var->Copy();//Create a new one with default values.
|
||||
|
||||
newVar->m_Weight = d;
|
||||
xform->AddVariation(newVar);
|
||||
items[0]->setBackgroundColor(0, QColor(200, 200, 200));//Set background to gray when a variation has non-zero weight in this xform.
|
||||
|
||||
//If they've added a new parametric variation, then grab the values currently in the spinners
|
||||
//for the child parameters and assign them to the newly added variation.
|
||||
if (parVar)
|
||||
{
|
||||
ParametricVariation<T>* newParVar = dynamic_cast<ParametricVariation<T>*>(newVar);
|
||||
|
||||
if (!items.empty())//Get the tree widget for the parent variation.
|
||||
{
|
||||
for (int i = 0; i < items[0]->childCount(); i++)//Iterate through all of the children, which will be the params.
|
||||
{
|
||||
QTreeWidgetItem* childItem = items[0]->child(i);//Get the child.
|
||||
QWidget* itemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
|
||||
|
||||
if (VariationTreeDoubleSpinBox<T>* spinBox = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(itemWidget))//Cast the widget to the VariationTreeDoubleSpinBox type.
|
||||
{
|
||||
string s = childItem->text(0).toStdString();//Use the name of the child, and the value of the spinner widget to assign the param.
|
||||
|
||||
newParVar->SetParamVal(s.c_str(), spinBox->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fractorium::OnVariationSpinBoxValueChanged(double d) { m_Controller->VariationSpinBoxValueChanged(d); }
|
||||
|
||||
/// <summary>
|
||||
/// Fill the variation tree values from passed in xform and apply the current sorting mode.
|
||||
/// Called when the currently selected xform changes.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform whose variation values will be used to fill the tree</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FillVariationTreeWithXform(Xform<T>* xform)
|
||||
{
|
||||
QTreeWidget* tree = m_Fractorium->ui.VariationsTree;
|
||||
|
||||
for (unsigned int i = 0; i < tree->topLevelItemCount(); i++)
|
||||
{
|
||||
QTreeWidgetItem* item = tree->topLevelItem(i);
|
||||
string varName = item->text(0).toStdString();
|
||||
Variation<T>* var = xform->GetVariationByName(varName);//See if this variation in the tree was contained in the xform.
|
||||
ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(var);//Attempt cast to parametric variation for later.
|
||||
ParametricVariation<T>* origParVar = dynamic_cast<ParametricVariation<T>*>(m_VariationList.GetVariation(varName));
|
||||
QWidget* itemWidget = tree->itemWidget(item, 1);//Get the widget for the item.
|
||||
|
||||
if (VariationTreeDoubleSpinBox<T>* spinBox = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(itemWidget))//Cast the widget to the VariationTreeDoubleSpinBox type.
|
||||
{
|
||||
spinBox->SetValueStealth(var ? var->m_Weight : 0);//If the variation was present, set the spin box to its weight, else zero.
|
||||
item->setBackgroundColor(0, var ? QColor(200, 200, 200) : QColor(255, 255, 255));//Ensure background is always white if the value goes to zero, else gray if var present.
|
||||
|
||||
for (unsigned int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation.
|
||||
{
|
||||
T* param = NULL;
|
||||
QTreeWidgetItem* childItem = item->child(j);//Get the child.
|
||||
QWidget* childItemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
|
||||
|
||||
if (VariationTreeDoubleSpinBox<T>* childSpinBox = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(childItemWidget))//Cast the widget to the VariationTreeDoubleSpinBox type.
|
||||
{
|
||||
string s = childItem->text(0).toStdString();//Get the name of the child.
|
||||
|
||||
if (parVar)
|
||||
{
|
||||
if (param = parVar->GetParam(s.c_str()))//Retrieve pointer to the param.
|
||||
childSpinBox->SetValueStealth(*param);
|
||||
}
|
||||
else if (origParVar)//Parametric variation was not present in this xform, so set child values to defaults.
|
||||
{
|
||||
if (param = origParVar->GetParam(s.c_str()))
|
||||
childSpinBox->SetValueStealth(*param);
|
||||
else
|
||||
childSpinBox->SetValueStealth(0);//Will most likely never happen, but just to be safe.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Fractorium->OnTreeHeaderSectionClicked(m_Fractorium->m_VarSortMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the sorting to be either by variation ID, or by weight.
|
||||
/// If sorting by variation ID, repeated clicks will altername ascending or descending.
|
||||
/// Called when user clicks the tree headers.
|
||||
/// </summary>
|
||||
/// <param name="logicalIndex">Column index of the header clicked. Sort by name if 0, sort by weight if 1.</param>
|
||||
void Fractorium::OnTreeHeaderSectionClicked(int logicalIndex)
|
||||
{
|
||||
m_VarSortMode = logicalIndex;
|
||||
ui.VariationsTree->sortItems(m_VarSortMode, m_VarSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||
|
||||
if (logicalIndex == 1)
|
||||
ui.VariationsTree->scrollToTop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the text in the variation filter text box to only show variations whose names
|
||||
/// contain the substring.
|
||||
/// Called when the user types in the variation filter text box.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to filter on</param>
|
||||
void Fractorium::OnVariationsFilterLineEditTextChanged(const QString& text)
|
||||
{
|
||||
QTreeWidget* tree = ui.VariationsTree;
|
||||
|
||||
tree->setUpdatesEnabled(false);
|
||||
|
||||
for (unsigned int i = 0; i < tree->topLevelItemCount(); i++)
|
||||
{
|
||||
QTreeWidgetItem* item = tree->topLevelItem(i);
|
||||
QString varName = item->text(0);
|
||||
|
||||
item->setHidden(!varName.contains(text, Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
OnTreeHeaderSectionClicked(m_VarSortMode);//Must re-sort every time the filter changes.
|
||||
tree->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the variation name filter, which will display all variations.
|
||||
/// Called when clear variations filter button is clicked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnVariationsFilterClearButtonClicked(bool checked)
|
||||
{
|
||||
ui.VariationsFilterLineEdit->clear();
|
||||
}
|
175
Source/Fractorium/FractoriumXformsXaos.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the xforms xaos UI.
|
||||
/// </summary>
|
||||
void Fractorium::InitXformsXaosUI()
|
||||
{
|
||||
connect(ui.XaosToRadio, SIGNAL(toggled(bool)), this, SLOT(OnXaosFromToToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ClearXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXaosButtonClicked(bool)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the xaos table with the values from the current xform.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::FillXaosWithCurrentXform()
|
||||
{
|
||||
Xform<T>* currXform = CurrentXform();
|
||||
|
||||
if (!IsFinal(currXform))
|
||||
{
|
||||
for (int i = 0; i < m_Ember.XformCount(); i++)
|
||||
{
|
||||
DoubleSpinBox* spinBox = dynamic_cast<DoubleSpinBox*>(m_Fractorium->ui.XaosTable->cellWidget(i, 1));
|
||||
|
||||
//Fill in values column.
|
||||
if (m_Fractorium->ui.XaosToRadio->isChecked())//"To": Single xform, advance index.
|
||||
spinBox->SetValueStealth(currXform->Xaos(i));
|
||||
else//"From": Advance xforms, single index.
|
||||
if (currXform = m_Ember.GetXform(i))
|
||||
spinBox->SetValueStealth(currXform->Xaos(m_Fractorium->ui.CurrentXformCombo->currentIndex()));
|
||||
|
||||
//Fill in name column.
|
||||
Xform<T>* xform = m_Ember.GetXform(i);
|
||||
QTableWidgetItem* xformNameItem = m_Fractorium->ui.XaosTable->item(i, 0);
|
||||
|
||||
if (xform && xformNameItem)
|
||||
xformNameItem->setText(MakeXaosNameString(i));
|
||||
}
|
||||
}
|
||||
|
||||
m_Fractorium->ui.XaosTable->setEnabled(!IsFinal(currXform));//Disable if final, else enable.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return a xaos name string.
|
||||
/// </summary>
|
||||
/// <param name="i">The index of the xform whose xaos will be used</param>
|
||||
/// <returns>The xaos name string</returns>
|
||||
template <typename T>
|
||||
QString FractoriumEmberController<T>::MakeXaosNameString(unsigned int i)
|
||||
{
|
||||
Xform<T>* xform = m_Ember.GetXform(i);
|
||||
QString name;
|
||||
|
||||
if (xform)
|
||||
{
|
||||
int i = m_Ember.GetXformIndex(xform) + 1;//GUI is 1 indexed to avoid confusing the user.
|
||||
int curr = m_Fractorium->ui.CurrentXformCombo->currentIndex() + 1;
|
||||
|
||||
if (i != -1)
|
||||
{
|
||||
if (m_Fractorium->ui.XaosToRadio->isChecked())
|
||||
name = QString("From ") + QString::number(curr) + QString(" To ") + QString::number(i);
|
||||
else
|
||||
name = QString("From ") + QString::number(i) + QString(" To ") + QString::number(curr);
|
||||
|
||||
//if (xform->m_Name != "")
|
||||
// name = name + " (" + QString::fromStdString(xform->m_Name) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the xaos value.
|
||||
/// Called when any xaos spinner is changed.
|
||||
/// Different action taken based on the state of to/from radio button.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="sender">The DoubleSpinBox that triggered this event</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::XaosChanged(DoubleSpinBox* sender)
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
QTableWidget* xaosTable = m_Fractorium->ui.XaosTable;
|
||||
|
||||
if (!IsFinal(xform))//This should never get called for the final xform because the table will be disabled, but check just to be safe.
|
||||
{
|
||||
for (int i = 0; i < xaosTable->rowCount(); i++)//Find the spin box that triggered the event.
|
||||
{
|
||||
DoubleSpinBox* spinBox = dynamic_cast<DoubleSpinBox*>(xaosTable->cellWidget(i, 1));
|
||||
|
||||
if (spinBox == sender)
|
||||
{
|
||||
if (m_Fractorium->ui.XaosToRadio->isChecked())//"To": Single xform, advance index.
|
||||
{
|
||||
xform->SetXaos(i, spinBox->value());
|
||||
}
|
||||
else//"From": Advance xforms, single index.
|
||||
{
|
||||
if (xform = m_Ember.GetXform(i))//Single = is intentional.
|
||||
xform->SetXaos(m_Fractorium->ui.CurrentXformCombo->currentIndex(), spinBox->value());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Fractorium::OnXaosChanged(double d)
|
||||
{
|
||||
if (DoubleSpinBox* senderSpinBox = dynamic_cast<DoubleSpinBox*>(this->sender()))
|
||||
m_Controller->XaosChanged(senderSpinBox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update xaos display to use either "to" or "from" logic.
|
||||
/// Called when xaos to/from radio buttons checked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
void Fractorium::OnXaosFromToToggled(bool checked)
|
||||
{
|
||||
m_Controller->FillXaosWithCurrentXform();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear xaos table, recreate all spinners based on the xaos used by the current xform in the current ember.
|
||||
/// Called every time the current xform changes.
|
||||
/// </summary>
|
||||
void Fractorium::FillXaosTable()
|
||||
{
|
||||
int spinHeight = 20;
|
||||
|
||||
ui.XaosTable->setRowCount(m_Controller->XformCount());//This will grow or shrink the number of rows and call the destructor for previous DoubleSpinBoxes.
|
||||
|
||||
for (int i = 0; i < m_Controller->XformCount(); i++)
|
||||
{
|
||||
DoubleSpinBox* spinBox = new DoubleSpinBox(ui.XaosTable, spinHeight, 0.1);
|
||||
QTableWidgetItem* xformNameItem = new QTableWidgetItem(m_Controller->MakeXaosNameString(i));
|
||||
|
||||
spinBox->DoubleClick(true);
|
||||
spinBox->DoubleClickZero(1);
|
||||
spinBox->DoubleClickNonZero(0);
|
||||
ui.XaosTable->setItem(i, 0, xformNameItem);
|
||||
ui.XaosTable->setCellWidget(i, 1, spinBox);
|
||||
connect(spinBox, SIGNAL(valueChanged(double)), this, SLOT(OnXaosChanged(double)), Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all xaos from the current ember.
|
||||
/// Called when xaos to/from radio buttons checked.
|
||||
/// </summary>
|
||||
/// <param name="checked">Ignored</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ClearXaos()
|
||||
{
|
||||
UpdateCurrentXform([&] (Xform<T>* xform)
|
||||
{
|
||||
m_Ember.ClearXaos();
|
||||
});
|
||||
|
||||
//Can't just call FillXaosWithCurrentXform() because the current xform might the final.
|
||||
for (int i = 0; i < m_Ember.XformCount(); i++)
|
||||
if (DoubleSpinBox* spinBox = dynamic_cast<DoubleSpinBox*>(m_Fractorium->ui.XaosTable->cellWidget(i, 1)))
|
||||
spinBox->SetValueStealth(1.0);
|
||||
}
|
||||
|
||||
void Fractorium::OnClearXaosButtonClicked(bool checked) { m_Controller->ClearXaos(); }
|
254
Source/Fractorium/GLEmberController.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "GLEmberController.h"
|
||||
#include "FractoriumEmberController.h"
|
||||
#include "Fractorium.h"
|
||||
#include "GLWidget.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which assigns pointers to the main window and the GLWidget.
|
||||
/// </summary>
|
||||
/// <param name="fractorium">Pointer to the main window</param>
|
||||
/// <param name="glWidget">Pointer to the GLWidget</param>
|
||||
GLEmberControllerBase::GLEmberControllerBase(Fractorium* fractorium, GLWidget* glWidget)
|
||||
{
|
||||
m_Fractorium = fractorium;
|
||||
m_GL = glWidget;
|
||||
m_AffineType = AffinePre;
|
||||
m_HoverType = HoverNone;
|
||||
m_DragState = DragNone;
|
||||
m_DragModifier = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty destructor which does nothing.
|
||||
/// </summary>
|
||||
GLEmberControllerBase::~GLEmberControllerBase() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes the pointers to the main window the GLWidget to the base,
|
||||
/// then assigns the pointer to the parent controller.
|
||||
/// </summary>
|
||||
/// <param name="fractorium">Pointer to the main window</param>
|
||||
/// <param name="glWidget">Pointer to the GLWidget</param>
|
||||
/// <param name="controller">Pointer to the parent controller of the same template type</param>
|
||||
template <typename T>
|
||||
GLEmberController<T>::GLEmberController(Fractorium* fractorium, GLWidget* glWidget, FractoriumEmberController<T>* controller)
|
||||
: GLEmberControllerBase(fractorium, glWidget)
|
||||
{
|
||||
GridStep = T(1.0 / 8.0);
|
||||
m_FractoriumEmberController = controller;
|
||||
|
||||
m_HoverXform = NULL;
|
||||
m_SelectedXform = NULL;
|
||||
m_CenterDownX = 0;
|
||||
m_CenterDownY = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty destructor which does nothing.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
GLEmberController<T>::~GLEmberController() { }
|
||||
|
||||
/// <summary>
|
||||
/// Check that the final output size of the current ember matches the dimensions passed in.
|
||||
/// </summary>
|
||||
/// <param name="w">The width to compare to</param>
|
||||
/// <param name="h">The height to compare to</param>
|
||||
/// <returns>True if any don't match, else false if they are both equal.</returns>
|
||||
template <typename T>
|
||||
bool GLEmberController<T>::CheckForSizeMismatch(int w, int h)
|
||||
{
|
||||
return (m_FractoriumEmberController->FinalRasW() != w || m_FractoriumEmberController->FinalRasH() != h);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the scale.
|
||||
/// Used when dragging the right mouse button.
|
||||
/// </summary>
|
||||
/// <returns>The distance dragged in pixels</returns>
|
||||
template <typename T>
|
||||
T GLEmberController<T>::CalcScale()
|
||||
{
|
||||
//Can't operate using world coords here because every time scale changes, the world bounds change.
|
||||
//So must instead calculate distance traveled based on window coords, which do not change outside of resize events.
|
||||
v2T windowCenter((T)m_GL->width() / T(2), (T)m_GL->height() / T(2));
|
||||
v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y);
|
||||
v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y);
|
||||
|
||||
T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter);
|
||||
T lengthMouseDownFromCenterInPixels = glm::length(windowMouseDownDistanceFromCenter);
|
||||
|
||||
return lengthMousePosFromCenterInPixels - lengthMouseDownFromCenterInPixels;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the rotation.
|
||||
/// Used when dragging the right mouse button.
|
||||
/// </summary>
|
||||
/// <returns>The angular distance rotated from -180-180</returns>
|
||||
template <typename T>
|
||||
T GLEmberController<T>::CalcRotation()
|
||||
{
|
||||
T rotStart = NormalizeDeg180<T>(T(90) - (atan2(-m_MouseDownWorldPos.y, m_MouseDownWorldPos.x) * RAD_2_DEG_T));
|
||||
T rot = NormalizeDeg180<T>(T(90) - (atan2(-m_MouseWorldPos.y, m_MouseWorldPos.x) * RAD_2_DEG_T));
|
||||
|
||||
return rotStart - rot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snap the passed in world cartesian coordinate to the grid for rotation, scale or translation.
|
||||
/// </summary>
|
||||
/// <param name="vec">The world cartesian coordinate to be snapped</param>
|
||||
/// <returns>The snapped world cartesian coordinate</returns>
|
||||
template <typename T>
|
||||
typename v3T GLEmberController<T>::SnapToGrid(v3T& vec)
|
||||
{
|
||||
v3T ret;
|
||||
|
||||
ret.x = glm::round(vec.x / GridStep) * GridStep;
|
||||
ret.y = glm::round(vec.y / GridStep) * GridStep;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snap the passed in world cartesian coordinate to the grid for rotation only.
|
||||
/// </summary>
|
||||
/// <param name="vec">The world cartesian coordinate to be snapped</param>
|
||||
/// <param name="divisions">The divisions of a circle to use for snapping</param>
|
||||
/// <returns>The snapped world cartesian coordinate</returns>
|
||||
template <typename T>
|
||||
typename v3T GLEmberController<T>::SnapToNormalizedAngle(v3T& vec, unsigned int divisions)
|
||||
{
|
||||
T rsq, theta;
|
||||
T bestRsq = numeric_limits<T>::max();
|
||||
v3T c, best;
|
||||
|
||||
best.x = 1;
|
||||
best.y = 0;
|
||||
|
||||
for (unsigned int i = 0; i < divisions; i++)
|
||||
{
|
||||
theta = 2.0 * M_PI * (T)i / (T)divisions;
|
||||
c.x = cos(theta);
|
||||
c.y = sin(theta);
|
||||
rsq = glm::distance(vec, c);
|
||||
|
||||
if (rsq < bestRsq)
|
||||
{
|
||||
best = c;
|
||||
bestRsq = rsq;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert raster window coordinates to world cartesian coordinates.
|
||||
/// </summary>
|
||||
/// <param name="v">The window coordinates to convert</param>
|
||||
/// <param name="flip">True to flip vertically, else don't.</param>
|
||||
/// <returns>The converted world cartesian coordinates</returns>
|
||||
template <typename T>
|
||||
typename v3T GLEmberController<T>::WindowToWorld(v3T& v, bool flip)
|
||||
{
|
||||
v3T mouse(v.x, flip ? m_Viewport[3] - v.y : v.y, 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
|
||||
v3T newCoords = glm::unProject(mouse, m_Modelview, m_Projection, m_Viewport);//Perform the calculation.
|
||||
|
||||
newCoords.z = 0;//For some reason, unProject() always comes back with the z coordinate as something other than 0. It should be 0 at all times.
|
||||
return newCoords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Template specialization for querying the viewport, modelview and projection
|
||||
/// matrices as floats.
|
||||
/// </summary>
|
||||
template <>
|
||||
void GLEmberController<float>::QueryVMP()
|
||||
{
|
||||
m_GL->glGetIntegerv(GL_VIEWPORT, glm::value_ptr(m_Viewport));
|
||||
m_GL->glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(m_Modelview));
|
||||
m_GL->glGetFloatv(GL_PROJECTION_MATRIX, glm::value_ptr(m_Projection));
|
||||
}
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
/// <summary>
|
||||
/// Template specialization for querying the viewport, modelview and projection
|
||||
/// matrices as doubles.
|
||||
/// </summary>
|
||||
template <>
|
||||
void GLEmberController<double>::QueryVMP()
|
||||
{
|
||||
m_GL->glGetIntegerv(GL_VIEWPORT, glm::value_ptr(m_Viewport));
|
||||
m_GL->glGetDoublev(GL_MODELVIEW_MATRIX, glm::value_ptr(m_Modelview));
|
||||
m_GL->glGetDoublev(GL_PROJECTION_MATRIX, glm::value_ptr(m_Projection));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Template specialization for multiplying the current matrix
|
||||
/// by an m4<float>.
|
||||
/// </summary>
|
||||
template <>
|
||||
void GLEmberController<float>::MultMatrix(glm::detail::tmat4x4<float, glm::defaultp>& mat)
|
||||
{
|
||||
m_GL->glMultMatrixf(glm::value_ptr(mat));
|
||||
}
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
/// <summary>
|
||||
/// Template specialization for multiplying the current matrix
|
||||
/// by an m4<double>.
|
||||
/// </summary>
|
||||
template <>
|
||||
void GLEmberController<double>::MultMatrix(glm::detail::tmat4x4<double, glm::defaultp>& mat)
|
||||
{
|
||||
m_GL->glMultMatrixd(glm::value_ptr(mat));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Query the matrices currently being used.
|
||||
/// Debugging function, unused.
|
||||
/// </summary>
|
||||
/// <param name="print">True to print values, else false.</param>
|
||||
template <typename T>
|
||||
void GLEmberController<T>::QueryMatrices(bool print)
|
||||
{
|
||||
RendererBase* renderer = m_FractoriumEmberController->Renderer();
|
||||
|
||||
if (renderer)
|
||||
{
|
||||
double unitX = fabs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0;
|
||||
double unitY = fabs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0;
|
||||
|
||||
m_GL->glMatrixMode(GL_PROJECTION);
|
||||
m_GL->glPushMatrix();
|
||||
m_GL->glLoadIdentity();
|
||||
m_GL->glOrtho(-unitX, unitX, -unitY, unitY, -1, 1);
|
||||
m_GL->glMatrixMode(GL_MODELVIEW);
|
||||
m_GL->glPushMatrix();
|
||||
m_GL->glLoadIdentity();
|
||||
|
||||
QueryVMP();
|
||||
|
||||
m_GL->glMatrixMode(GL_PROJECTION);
|
||||
m_GL->glPopMatrix();
|
||||
m_GL->glMatrixMode(GL_MODELVIEW);
|
||||
m_GL->glPopMatrix();
|
||||
|
||||
if (print)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
qDebug() << "Viewport[" << i << "] = " << m_Viewport[i] << endl;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
qDebug() << "Modelview[" << i << "] = " << glm::value_ptr(m_Modelview)[i] << endl;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
qDebug() << "Projection[" << i << "] = " << glm::value_ptr(m_Projection)[i] << endl;
|
||||
}
|
||||
}
|
||||
}
|
146
Source/Fractorium/GLEmberController.h
Normal file
@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// GLEmberControllerBase and GLEmberController<T> classes.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Use/draw pre or post affine transform.
|
||||
/// </summary>
|
||||
enum eAffineType { AffinePre, AffinePost };
|
||||
|
||||
/// <summary>
|
||||
/// Hovering over nothing, the x axis, the y axis or the center.
|
||||
/// </summary>
|
||||
enum eHoverType { HoverNone, HoverXAxis, HoverYAxis, HoverTranslation };
|
||||
|
||||
/// <summary>
|
||||
/// Dragging an affine transform or panning, rotating or scaling the image.
|
||||
/// </summary>
|
||||
enum eDragState { DragNone, DragPanning, DragDragging, DragRotateScale };
|
||||
|
||||
/// <summary>
|
||||
/// Dragging with no keys pressed, shift, control or alt.
|
||||
/// </summary>
|
||||
enum eDragModifier { DragModNone = 0x00, DragModShift = 0x01, DragModControl = 0x02, DragModAlt = 0x04 };
|
||||
|
||||
/// <summary>
|
||||
/// GLController, FractoriumEmberController, GLWidget and Fractorium need each other, but each can't all include the other.
|
||||
/// So GLWidget includes this file, and GLWidget, FractoriumEmberController and Fractorium are declared as forward declarations here.
|
||||
/// </summary>
|
||||
class GLWidget;
|
||||
class Fractorium;
|
||||
template <typename T> class FractoriumEmberController;
|
||||
|
||||
/// <summary>
|
||||
/// GLEmberControllerBase serves as a non-templated base class with virtual
|
||||
/// functions which will be overridden in a derived class that takes a template parameter.
|
||||
/// The controller serves as a way to access both the GLWidget and the underlying ember
|
||||
/// objects through an interface that doesn't require template argument, but does allow
|
||||
/// templated objects to be used underneath.
|
||||
/// The functions not implemented in this file can be found in GLWidget.cpp near the area of code which uses them.
|
||||
/// </summary>
|
||||
class GLEmberControllerBase
|
||||
{
|
||||
public:
|
||||
GLEmberControllerBase(Fractorium* fractorium, GLWidget* glWidget);
|
||||
virtual ~GLEmberControllerBase();
|
||||
|
||||
void ClearDrag();
|
||||
bool Allocate(bool force = false);
|
||||
|
||||
virtual void DrawImage() { }
|
||||
virtual void DrawAffines(bool pre, bool post) { }
|
||||
virtual void ClearWindow() { }
|
||||
virtual bool KeyPress(QKeyEvent* e);
|
||||
virtual bool KeyRelease(QKeyEvent* e);
|
||||
virtual void MousePress(QMouseEvent* e) { }
|
||||
virtual void MouseRelease(QMouseEvent* e) { }
|
||||
virtual void MouseMove(QMouseEvent* e) { }
|
||||
virtual void Wheel(QWheelEvent* e) { }
|
||||
virtual bool SyncSizes() { return false; }
|
||||
virtual bool CheckForSizeMismatch(int w, int h) { return true; }
|
||||
virtual void QueryMatrices(bool print) { }
|
||||
|
||||
protected:
|
||||
unsigned int m_DragModifier;
|
||||
glm::ivec2 m_MousePos;
|
||||
glm::ivec2 m_MouseDownPos;
|
||||
glm::ivec4 m_Viewport;
|
||||
eDragState m_DragState;
|
||||
eHoverType m_HoverType;
|
||||
eAffineType m_AffineType;
|
||||
GLWidget* m_GL;
|
||||
Fractorium* m_Fractorium;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Templated derived class which implements all interaction functionality between the embers
|
||||
/// of a specific template type and the GLWidget;
|
||||
/// </summary>
|
||||
template<typename T>
|
||||
class GLEmberController : public GLEmberControllerBase
|
||||
{
|
||||
public:
|
||||
GLEmberController(Fractorium* fractorium, GLWidget* glWidget, FractoriumEmberController<T>* controller);
|
||||
virtual ~GLEmberController();
|
||||
virtual void DrawImage();
|
||||
virtual void DrawAffines(bool pre, bool post);
|
||||
virtual void ClearWindow();
|
||||
virtual void MousePress(QMouseEvent* e);
|
||||
virtual void MouseRelease(QMouseEvent* e);
|
||||
virtual void MouseMove(QMouseEvent* e);
|
||||
virtual void Wheel(QWheelEvent* e);
|
||||
virtual void QueryMatrices(bool print);
|
||||
virtual bool SyncSizes();
|
||||
virtual bool CheckForSizeMismatch(int w, int h);
|
||||
|
||||
T CalcScale();
|
||||
T CalcRotation();
|
||||
Affine2D<T> CalcDragXAxis();
|
||||
Affine2D<T> CalcDragYAxis();
|
||||
Affine2D<T> CalcDragTranslation();
|
||||
|
||||
void SetEmber(Ember<T>* ember);
|
||||
void SetSelectedXform(Xform<T>* xform);
|
||||
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);
|
||||
|
||||
private:
|
||||
v3T SnapToGrid(v3T& vec);
|
||||
v3T SnapToNormalizedAngle(v3T& vec, unsigned int divisions);
|
||||
v3T WindowToWorld(v3T& v, bool flip);
|
||||
void QueryVMP();
|
||||
void MultMatrix(m4T& mat);
|
||||
|
||||
T m_CenterDownX;
|
||||
T m_CenterDownY;
|
||||
T m_RotationDown;
|
||||
T m_ScaleDown;
|
||||
v4T m_BoundsDown;
|
||||
|
||||
v3T m_MouseWorldPos;
|
||||
v3T m_MouseDownWorldPos;
|
||||
v3T m_DragHandlePos;
|
||||
v3T m_DragHandleOffset;
|
||||
v3T m_HoverHandlePos;
|
||||
|
||||
m4T m_Modelview;
|
||||
m4T m_Projection;
|
||||
|
||||
Affine2D<T> m_DragSrcTransform;
|
||||
|
||||
Xform<T>* m_HoverXform;
|
||||
Xform<T>* m_SelectedXform;
|
||||
FractoriumEmberController<T>* m_FractoriumEmberController;
|
||||
T GridStep;
|
||||
};
|
||||
|
||||
template class GLEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template class GLEmberController<double>;
|
||||
#endif
|
1435
Source/Fractorium/GLWidget.cpp
Normal file
76
Source/Fractorium/GLWidget.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumEmberController.h"
|
||||
|
||||
/// <summary>
|
||||
/// GLWidget class.
|
||||
/// </summary>
|
||||
|
||||
class Fractorium;//Forward declaration since Fractorium uses this widget.
|
||||
|
||||
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
|
||||
/// size matches the size of the window.
|
||||
/// On top of that, the circles that represent the pre and post affine transforms for each xform are drawn.
|
||||
/// Based on values specified on the GUI, it will either draw the presently selected xform, or all of them.
|
||||
/// It can show/hide pre/post affine as well.
|
||||
/// The currently selected xform is drawn with a circle around it, with all others only showing their axes.
|
||||
/// The current xform is set by either clicking on it, or by changing the index of the xforms combo box on the main window.
|
||||
/// A problem here is that all drawing is done using the legacy OpenGL fixed function pipeline which is deprecated
|
||||
/// and even completely disabled on Mac OS. This will need to be replaced with shader programs for every draw operation.
|
||||
/// Since this window has to know about various states of the renderer and the main window, it retains pointers to
|
||||
/// the main window and several of its members.
|
||||
/// This class uses a controller-based design similar to the main window.
|
||||
/// </summary>
|
||||
class GLWidget : public QGLWidget, protected QOpenGLFunctions_2_0//QOpenGLFunctions_3_2_Compatibility//QOpenGLFunctions_3_2_Core//, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend Fractorium;
|
||||
friend FractoriumEmberController<float>;
|
||||
friend FractoriumEmberController<double>;
|
||||
friend GLEmberControllerBase;
|
||||
friend GLEmberController<float>;
|
||||
friend GLEmberController<double>;
|
||||
|
||||
public:
|
||||
GLWidget(QWidget* parent);
|
||||
~GLWidget();
|
||||
void DrawQuad();
|
||||
void SetMainWindow(Fractorium* f);
|
||||
bool Init();
|
||||
bool Drawing();
|
||||
GLuint OutputTexID();
|
||||
|
||||
protected:
|
||||
virtual void initializeGL();
|
||||
virtual void paintGL();
|
||||
virtual void keyPressEvent(QKeyEvent* e);
|
||||
virtual void keyReleaseEvent(QKeyEvent* e);
|
||||
virtual void mousePressEvent(QMouseEvent* e);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* e);
|
||||
virtual void mouseMoveEvent(QMouseEvent* e);
|
||||
virtual void wheelEvent(QWheelEvent* e);
|
||||
virtual void resizeEvent(QResizeEvent* e);
|
||||
|
||||
private:
|
||||
bool Allocate(bool force = false);
|
||||
bool Deallocate();
|
||||
void SetViewport();
|
||||
void DrawGrid();
|
||||
void DrawUnitSquare();
|
||||
void DrawAffineHelper(bool selected, bool pre, bool final, bool background);
|
||||
GLEmberControllerBase* GLController();
|
||||
|
||||
bool m_Init;
|
||||
bool m_Drawing;
|
||||
GLint m_TexWidth;
|
||||
GLint m_TexHeight;
|
||||
GLint m_ViewWidth;
|
||||
GLint m_ViewHeight;
|
||||
GLuint m_OutputTexID;
|
||||
Fractorium* m_Fractorium;
|
||||
};
|
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 147 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.4 KiB |
BIN
Source/Fractorium/Icons/Fractorium.ico
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
Source/Fractorium/Icons/add.png
Normal file
After Width: | Height: | Size: 911 B |
BIN
Source/Fractorium/Icons/application_side_boxes.png
Normal file
After Width: | Height: | Size: 482 B |
BIN
Source/Fractorium/Icons/arrow-redo.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Source/Fractorium/Icons/arrow-undo.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Source/Fractorium/Icons/arrow_down.png
Normal file
After Width: | Height: | Size: 460 B |
BIN
Source/Fractorium/Icons/arrow_in.png
Normal file
After Width: | Height: | Size: 538 B |
BIN
Source/Fractorium/Icons/arrow_left.png
Normal file
After Width: | Height: | Size: 398 B |
BIN
Source/Fractorium/Icons/arrow_out.png
Normal file
After Width: | Height: | Size: 531 B |
BIN
Source/Fractorium/Icons/arrow_right.png
Normal file
After Width: | Height: | Size: 404 B |
BIN
Source/Fractorium/Icons/arrow_rotate_anticlockwise.png
Normal file
After Width: | Height: | Size: 584 B |
BIN
Source/Fractorium/Icons/arrow_rotate_clockwise.png
Normal file
After Width: | Height: | Size: 563 B |
BIN
Source/Fractorium/Icons/arrow_turn_left.png
Normal file
After Width: | Height: | Size: 532 B |
BIN
Source/Fractorium/Icons/arrow_turn_right.png
Normal file
After Width: | Height: | Size: 514 B |
BIN
Source/Fractorium/Icons/arrow_up.png
Normal file
After Width: | Height: | Size: 459 B |
BIN
Source/Fractorium/Icons/banner.bmp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
Source/Fractorium/Icons/cog.png
Normal file
After Width: | Height: | Size: 512 B |
BIN
Source/Fractorium/Icons/configure.png
Normal file
After Width: | Height: | Size: 1016 B |
BIN
Source/Fractorium/Icons/database-import.png
Normal file
After Width: | Height: | Size: 722 B |
BIN
Source/Fractorium/Icons/database-medium.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Source/Fractorium/Icons/databases.png
Normal file
After Width: | Height: | Size: 661 B |
BIN
Source/Fractorium/Icons/del.png
Normal file
After Width: | Height: | Size: 643 B |
BIN
Source/Fractorium/Icons/dialog.bmp
Normal file
After Width: | Height: | Size: 451 KiB |
BIN
Source/Fractorium/Icons/display-brightness-off.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Source/Fractorium/Icons/document-hf-insert.png
Normal file
After Width: | Height: | Size: 498 B |
BIN
Source/Fractorium/Icons/drive-harddisk-5.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Source/Fractorium/Icons/editraise.png
Normal file
After Width: | Height: | Size: 932 B |
BIN
Source/Fractorium/Icons/eraser.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Source/Fractorium/Icons/folder-visiting-4.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Source/Fractorium/Icons/infomation.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Source/Fractorium/Icons/layer--plus.png
Normal file
After Width: | Height: | Size: 614 B |
BIN
Source/Fractorium/Icons/layers-stack.png
Normal file
After Width: | Height: | Size: 739 B |
BIN
Source/Fractorium/Icons/layers.png
Normal file
After Width: | Height: | Size: 634 B |
BIN
Source/Fractorium/Icons/monitor.png
Normal file
After Width: | Height: | Size: 647 B |
BIN
Source/Fractorium/Icons/page_copy.png
Normal file
After Width: | Height: | Size: 634 B |
BIN
Source/Fractorium/Icons/page_paste.png
Normal file
After Width: | Height: | Size: 675 B |
BIN
Source/Fractorium/Icons/proxy.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Source/Fractorium/Icons/shape_flip_horizontal.png
Normal file
After Width: | Height: | Size: 459 B |
BIN
Source/Fractorium/Icons/shape_flip_vertical.png
Normal file
After Width: | Height: | Size: 438 B |
BIN
Source/Fractorium/Icons/stop.png
Normal file
After Width: | Height: | Size: 677 B |
BIN
Source/Fractorium/Icons/window-close.png
Normal file
After Width: | Height: | Size: 582 B |
25
Source/Fractorium/Main.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "Fractorium.h"
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
/// <summary>
|
||||
/// Main program entry point for Fractorium.exe.
|
||||
/// </summary>
|
||||
/// <param name="argc">The number of command line arguments passed</param>
|
||||
/// <param name="argv">The command line arguments passed</param>
|
||||
/// <returns>0 if successful, else 1.</returns>
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
#ifdef TEST_CL
|
||||
QMessageBox::critical(QApplication::desktop(), "Error", "Fractorium cannot be run in test mode, undefine TEST_CL 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.
|
||||
_putenv_s("GPU_MAX_ALLOC_PERCENT", "100");
|
||||
Fractorium w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
210
Source/Fractorium/OptionsDialog.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "OptionsDialog.h"
|
||||
#include "Fractorium.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a pointer to the settings object and the parent widget.
|
||||
/// </summary>
|
||||
/// <param name="settings">A pointer to the settings object to use</param>
|
||||
/// <param name="parent">The parent widget. Default: NULL.</param>
|
||||
/// <param name="f">The window flags. Default: 0.</param>
|
||||
FractoriumOptionsDialog::FractoriumOptionsDialog(FractoriumSettings* settings, QWidget* parent, Qt::WindowFlags f)
|
||||
: QDialog(parent, f)
|
||||
{
|
||||
int row = 0, spinHeight = 20;
|
||||
unsigned int i;
|
||||
|
||||
ui.setupUi(this);
|
||||
m_Settings = settings;
|
||||
QTableWidget* table = ui.OptionsXmlSavingTable;
|
||||
ui.ThreadCountSpin->setRange(1, Timing::ProcessorCount());
|
||||
connect(ui.OpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui.PlatformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnPlatformComboCurrentIndexChanged(int)), Qt::QueuedConnection);
|
||||
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlWidthSpin, spinHeight, 10, 100000, 50, "", "", true, 1920);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlHeightSpin, spinHeight, 10, 100000, 50, "", "", true, 1080);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlTemporalSamplesSpin, spinHeight, 1, 1000, 100, "", "", true, 1000);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlQualitySpin, spinHeight, 1, 200000, 50, "", "", true, 1000);
|
||||
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlSupersampleSpin, spinHeight, 1, 4, 1, "", "", true, 2);
|
||||
|
||||
m_IdEdit = new QLineEdit(ui.OptionsIdentityTable);
|
||||
ui.OptionsIdentityTable->setCellWidget(0, 1, m_IdEdit);
|
||||
|
||||
m_UrlEdit = new QLineEdit(ui.OptionsIdentityTable);
|
||||
ui.OptionsIdentityTable->setCellWidget(1, 1, m_UrlEdit);
|
||||
|
||||
m_NickEdit = new QLineEdit(ui.OptionsIdentityTable);
|
||||
ui.OptionsIdentityTable->setCellWidget(2, 1, m_NickEdit);
|
||||
|
||||
//QWidget::setTabOrder(m_IdEdit, m_UrlEdit);
|
||||
//QWidget::setTabOrder(m_UrlEdit, m_NickEdit);
|
||||
//QWidget::setTabOrder(m_NickEdit, m_IdEdit);
|
||||
|
||||
m_IdEdit->setText(m_Settings->Id());
|
||||
m_UrlEdit->setText(m_Settings->Url());
|
||||
m_NickEdit->setText(m_Settings->Nick());
|
||||
|
||||
if (m_Wrapper.CheckOpenCL())
|
||||
{
|
||||
vector<string> platforms = m_Wrapper.PlatformNames();
|
||||
|
||||
//Populate combo boxes with available OpenCL platforms and devices.
|
||||
for (i = 0; i < platforms.size(); i++)
|
||||
ui.PlatformCombo->addItem(QString::fromStdString(platforms[i]));
|
||||
|
||||
//If init succeeds, set the selected platform and device combos to match what was saved in the settings.
|
||||
if (m_Wrapper.Init(m_Settings->PlatformIndex(), m_Settings->DeviceIndex()))
|
||||
{
|
||||
ui.OpenCLCheckBox->setChecked( m_Settings->OpenCL());
|
||||
ui.PlatformCombo->setCurrentIndex(m_Settings->PlatformIndex());
|
||||
ui.DeviceCombo->setCurrentIndex( m_Settings->DeviceIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
OnPlatformComboCurrentIndexChanged(0);
|
||||
ui.OpenCLCheckBox->setChecked(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui.OpenCLCheckBox->setChecked(false);
|
||||
ui.OpenCLCheckBox->setEnabled(false);
|
||||
}
|
||||
|
||||
ui.EarlyClipCheckBox->setChecked( m_Settings->EarlyClip());
|
||||
ui.TransparencyCheckBox->setChecked(m_Settings->Transparency());
|
||||
ui.DoublePrecisionCheckBox->setChecked( m_Settings->Double());
|
||||
ui.ThreadCountSpin->setValue( m_Settings->ThreadCount());
|
||||
|
||||
if (m_Settings->CpuDEFilter())
|
||||
ui.CpuFilteringDERadioButton->setChecked(true);
|
||||
else
|
||||
ui.CpuFilteringLogRadioButton->setChecked(true);
|
||||
|
||||
if (m_Settings->OpenCLDEFilter())
|
||||
ui.OpenCLFilteringDERadioButton->setChecked(true);
|
||||
else
|
||||
ui.OpenCLFilteringLogRadioButton->setChecked(true);
|
||||
|
||||
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
|
||||
ui.OpenCLSubBatchSpin->setValue(m_Settings->OpenCLSubBatch());
|
||||
|
||||
m_XmlWidthSpin->setValue(m_Settings->XmlWidth());
|
||||
m_XmlHeightSpin->setValue(m_Settings->XmlHeight());
|
||||
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
|
||||
m_XmlQualitySpin->setValue(m_Settings->XmlQuality());
|
||||
m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample());
|
||||
|
||||
OnOpenCLCheckBoxStateChanged(ui.OpenCLCheckBox->isChecked());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GUI settings wrapper functions, getters only.
|
||||
/// </summary>
|
||||
|
||||
bool FractoriumOptionsDialog::EarlyClip() { return ui.EarlyClipCheckBox->isChecked(); }
|
||||
bool FractoriumOptionsDialog::Transparency() { return ui.TransparencyCheckBox->isChecked(); }
|
||||
bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked(); }
|
||||
bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); }
|
||||
unsigned int FractoriumOptionsDialog::PlatformIndex() { return ui.PlatformCombo->currentIndex(); }
|
||||
unsigned int FractoriumOptionsDialog::DeviceIndex() { return ui.DeviceCombo->currentIndex(); }
|
||||
unsigned int FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
|
||||
|
||||
/// <summary>
|
||||
/// Disable or enable the OpenCL related controls based on the state passed in.
|
||||
/// Called when the state of the OpenCL checkbox is changed.
|
||||
/// </summary>
|
||||
/// <param name="state">The state of the checkbox</param>
|
||||
void FractoriumOptionsDialog::OnOpenCLCheckBoxStateChanged(int state)
|
||||
{
|
||||
bool checked = state == Qt::Checked;
|
||||
|
||||
ui.PlatformCombo->setEnabled(checked);
|
||||
ui.DeviceCombo->setEnabled(checked);
|
||||
ui.ThreadCountSpin->setEnabled(!checked);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the the device combo box with all available
|
||||
/// OpenCL devices for the selected platform.
|
||||
/// Called when the platform combo box index changes.
|
||||
/// </summary>
|
||||
/// <param name="index">The selected index of the combo box</param>
|
||||
void FractoriumOptionsDialog::OnPlatformComboCurrentIndexChanged(int index)
|
||||
{
|
||||
vector<string> devices = m_Wrapper.DeviceNames(index);
|
||||
|
||||
ui.DeviceCombo->clear();
|
||||
|
||||
for (size_t i = 0; i < devices.size(); i++)
|
||||
ui.DeviceCombo->addItem(QString::fromStdString(devices[i]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save all settings on the GUI to the settings object.
|
||||
/// Called when the user clicks ok.
|
||||
/// Not called if cancelled or closed with the X.
|
||||
/// </summary>
|
||||
void FractoriumOptionsDialog::accept()
|
||||
{
|
||||
//Interactive rendering.
|
||||
m_Settings->EarlyClip(EarlyClip());
|
||||
m_Settings->Transparency(Transparency());
|
||||
m_Settings->OpenCL(OpenCL());
|
||||
m_Settings->Double(Double());
|
||||
m_Settings->PlatformIndex(PlatformIndex());
|
||||
m_Settings->DeviceIndex(DeviceIndex());
|
||||
m_Settings->ThreadCount(ThreadCount());
|
||||
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
|
||||
m_Settings->OpenCLSubBatch(ui.OpenCLSubBatchSpin->value());
|
||||
m_Settings->CpuDEFilter(ui.CpuFilteringDERadioButton->isChecked());
|
||||
m_Settings->OpenCLDEFilter(ui.OpenCLFilteringDERadioButton->isChecked());
|
||||
|
||||
//Xml saving.
|
||||
m_Settings->XmlWidth(m_XmlWidthSpin->value());
|
||||
m_Settings->XmlHeight(m_XmlHeightSpin->value());
|
||||
m_Settings->XmlTemporalSamples(m_XmlTemporalSamplesSpin->value());
|
||||
m_Settings->XmlQuality(m_XmlQualitySpin->value());
|
||||
m_Settings->XmlSupersample(m_XmlSupersampleSpin->value());
|
||||
|
||||
//Identity.
|
||||
m_Settings->Id(m_IdEdit->text());
|
||||
m_Settings->Url(m_UrlEdit->text());
|
||||
m_Settings->Nick(m_NickEdit->text());
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore all GUI items to what was originally in the settings object.
|
||||
/// Called when the user clicks cancel or closes with the X.
|
||||
/// </summary>
|
||||
void FractoriumOptionsDialog::reject()
|
||||
{
|
||||
//Interactive rendering.
|
||||
ui.EarlyClipCheckBox->setChecked(m_Settings->EarlyClip());
|
||||
ui.TransparencyCheckBox->setChecked(m_Settings->Transparency());
|
||||
ui.OpenCLCheckBox->setChecked(m_Settings->OpenCL());
|
||||
ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double());
|
||||
ui.PlatformCombo->setCurrentIndex(m_Settings->PlatformIndex());
|
||||
ui.DeviceCombo->setCurrentIndex(m_Settings->DeviceIndex());
|
||||
ui.ThreadCountSpin->setValue(m_Settings->ThreadCount());
|
||||
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
|
||||
ui.OpenCLSubBatchSpin->setValue(m_Settings->OpenCLSubBatch());
|
||||
ui.CpuFilteringDERadioButton->setChecked(m_Settings->CpuDEFilter());
|
||||
ui.OpenCLFilteringDERadioButton->setChecked(m_Settings->OpenCLDEFilter());
|
||||
|
||||
//Xml saving.
|
||||
m_XmlWidthSpin->setValue(m_Settings->XmlWidth());
|
||||
m_XmlHeightSpin->setValue(m_Settings->XmlHeight());
|
||||
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
|
||||
m_XmlQualitySpin->setValue(m_Settings->XmlQuality());
|
||||
m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample());
|
||||
|
||||
//Identity.
|
||||
m_IdEdit->setText(m_Settings->Id());
|
||||
m_UrlEdit->setText(m_Settings->Url());
|
||||
m_NickEdit->setText(m_Settings->Nick());
|
||||
|
||||
QDialog::reject();
|
||||
}
|
56
Source/Fractorium/OptionsDialog.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_OptionsDialog.h"
|
||||
#include "FractoriumSettings.h"
|
||||
#include "SpinBox.h"
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumOptionsDialog class.
|
||||
/// </summary>
|
||||
|
||||
class Fractorium;//Forward declaration since Fractorium uses this dialog.
|
||||
|
||||
/// <summary>
|
||||
/// The options dialog allows the user to save various preferences
|
||||
/// between program runs.
|
||||
/// It has a pointer to a FractoriumSettings object which is assigned
|
||||
/// in the constructor. The main window holds the object as a member and the
|
||||
/// pointer to it here is just for convenience.
|
||||
/// </summary>
|
||||
class FractoriumOptionsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend Fractorium;
|
||||
|
||||
public:
|
||||
FractoriumOptionsDialog(FractoriumSettings* settings, QWidget* parent = 0, Qt::WindowFlags f = 0);
|
||||
|
||||
public slots:
|
||||
void OnOpenCLCheckBoxStateChanged(int state);
|
||||
void OnPlatformComboCurrentIndexChanged(int index);
|
||||
virtual void accept();
|
||||
virtual void reject();
|
||||
|
||||
private:
|
||||
bool EarlyClip();
|
||||
bool AlphaChannel();
|
||||
bool Transparency();
|
||||
bool OpenCL();
|
||||
bool Double();
|
||||
unsigned int PlatformIndex();
|
||||
unsigned int DeviceIndex();
|
||||
unsigned int ThreadCount();
|
||||
|
||||
Ui::OptionsDialog ui;
|
||||
OpenCLWrapper m_Wrapper;
|
||||
SpinBox* m_XmlWidthSpin;
|
||||
SpinBox* m_XmlHeightSpin;
|
||||
SpinBox* m_XmlTemporalSamplesSpin;
|
||||
SpinBox* m_XmlQualitySpin;
|
||||
SpinBox* m_XmlSupersampleSpin;
|
||||
QLineEdit* m_IdEdit;
|
||||
QLineEdit* m_UrlEdit;
|
||||
QLineEdit* m_NickEdit;
|
||||
FractoriumSettings* m_Settings;
|
||||
};
|
746
Source/Fractorium/OptionsDialog.ui
Normal file
@ -0,0 +1,746 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OptionsDialog</class>
|
||||
<widget class="QDialog" name="OptionsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>286</width>
|
||||
<height>388</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>286</width>
|
||||
<height>388</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>286</width>
|
||||
<height>388</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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>
|
||||
<widget class="QTabWidget" name="OptionsTabWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Triangular</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="OptionsInteractiveRenderingTab">
|
||||
<attribute name="title">
|
||||
<string>Interactive Rendering</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="verticalSpacing">
|
||||
<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 row="0" column="0">
|
||||
<widget class="QCheckBox" name="EarlyClipCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Checked: clip colors and gamma correct after density filtering.</p><p>Unchecked: do it after spatial filtering.</p></body></html></string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Early Clip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="TransparencyCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use transparency in the final image.</p><p>This will not make a difference in the editor, but will when saving as .png and opening in other programs.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Transparency</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="OpenCLCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use OpenCL to render if your video card supports it.</p><p>This is highly recommended as it will give fluid, real-time interactive editing.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use OpenCL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QComboBox" name="PlatformCombo"/>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QComboBox" name="DeviceCombo"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QSpinBox" name="ThreadCountSpin">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The number of threads to use with CPU rendering.</p><p>Decrease for more responsive editing, increase for better performance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>Threads </string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QGroupBox" name="InteraciveCpuFilteringGroupBox">
|
||||
<property name="title">
|
||||
<string>CPU Filtering</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="CpuFilteringLogRadioButton">
|
||||
<property name="toolTip">
|
||||
<string>Use log scale filtering for interactive editing on the CPU.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Log Scale</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="CpuFilteringDERadioButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use full density estimation filtering for interactive editing on the CPU.</p><p>This is slower, but gives better feedback.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Full DE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QGroupBox" name="InteraciveGpuFilteringGroupBox">
|
||||
<property name="title">
|
||||
<string>OpenCL Filtering</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="OpenCLFilteringLogRadioButton">
|
||||
<property name="toolTip">
|
||||
<string>Use log scale filtering for interactive editing using OpenCL.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Log Scale</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="OpenCLFilteringDERadioButton">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use full density estimation filtering for interactive editing using OpenCL.</p><p>This is slower, but gives better feedback.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Full DE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QSpinBox" name="CpuSubBatchSpin">
|
||||
<property name="toolTip">
|
||||
<string>The number of 10,000 iteration chunks ran per thread on the CPU
|
||||
in interactive mode for each mouse movement</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>CPU Sub Batch </string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QSpinBox" name="OpenCLSubBatchSpin">
|
||||
<property name="toolTip">
|
||||
<string>The number of ~8M iteration chunks ran using OpenCL
|
||||
in interactive mode for each mouse movement</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>OpenCL Sub Batch </string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="DoublePrecisionCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Checked: use 64-bit double precision numbers (slower, but better image quality).</p><p>Unchecked: use 32-bit single precision numbers (faster, but worse image quality).</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Double Precision</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="OptionsXmlSavingTab">
|
||||
<attribute name="title">
|
||||
<string>Xml Saving</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_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 row="0" column="0" colspan="2">
|
||||
<widget class="TableWidget" name="OptionsXmlSavingTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>122</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>122</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</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="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::SolidLine</enum>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>110</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>35</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Temporal Samples</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Quality</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Supersample</string>
|
||||
</property>
|
||||
</row>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Field</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
<item row="0" column="0">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<property name="text">
|
||||
<string>Temporal Samples</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<property name="text">
|
||||
<string>Quality</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<property name="text">
|
||||
<string>Supersample</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="OptionsIdentityTab">
|
||||
<attribute name="title">
|
||||
<string>Identity</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<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 row="0" column="0" colspan="2">
|
||||
<widget class="TableWidget" name="OptionsIdentityTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>74</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>74</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</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="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::SolidLine</enum>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>110</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>35</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Id</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Url</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Nick</string>
|
||||
</property>
|
||||
</row>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Field</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
<item row="0" column="0">
|
||||
<property name="text">
|
||||
<string>Id</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<property name="text">
|
||||
<string>Url</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<property name="text">
|
||||
<string>Nick</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
<property name="flags">
|
||||
<set>ItemIsSelectable|ItemIsEditable|ItemIsDragEnabled|ItemIsUserCheckable|ItemIsEnabled</set>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="OptionsButtonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TableWidget</class>
|
||||
<extends>QTableWidget</extends>
|
||||
<header>TableWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>OptionsButtonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>OptionsDialog</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>OptionsButtonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OptionsDialog</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>
|
205
Source/Fractorium/SpinBox.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "SpinBox.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that passes parent to the base and sets up height and step.
|
||||
/// Specific focus policy is used to allow the user to hover over the control
|
||||
/// and change its value using the mouse wheel without explicitly having to click
|
||||
/// inside of it.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget. Default: NULL.</param>
|
||||
/// <param name="height">The height of the spin box. Default: 16.</param>
|
||||
/// <param name="step">The step used to increment/decrement the spin box when using the mouse wheel. Default: 1.</param>
|
||||
SpinBox::SpinBox(QWidget* parent, int height, int step)
|
||||
: QSpinBox(parent)
|
||||
{
|
||||
m_Select = false;
|
||||
m_DoubleClick = false;
|
||||
m_DoubleClickNonZero = 0;
|
||||
m_DoubleClickZero = 1;
|
||||
m_Step = step;
|
||||
m_SmallStep = 1;
|
||||
setSingleStep(step);
|
||||
setFrame(false);
|
||||
setButtonSymbols(QAbstractSpinBox::NoButtons);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMinimumHeight(height);//setGeometry() has no effect, so set both of these instead.
|
||||
setMaximumHeight(height);
|
||||
lineEdit()->installEventFilter(this);
|
||||
lineEdit()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
connect(this, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxValueChanged(int)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the control without triggering signals.
|
||||
/// </summary>
|
||||
/// <param name="d">The value to set it to</param>
|
||||
void SpinBox::SetValueStealth(int d)
|
||||
{
|
||||
blockSignals(true);
|
||||
setValue(d);
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set whether to respond to double click events.
|
||||
/// </summary>
|
||||
/// <param name="b">True if this should respond to double click events, else false.</param>
|
||||
void SpinBox::DoubleClick(bool b)
|
||||
{
|
||||
m_DoubleClick = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used when the user double clicks the spinner while
|
||||
/// it contains zero.
|
||||
/// </summary>
|
||||
/// <param name="val">The value to be used</param>
|
||||
void SpinBox::DoubleClickZero(int val)
|
||||
{
|
||||
m_DoubleClickZero = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value to be used when the user double clicks the spinner while
|
||||
/// it contains a non-zero value.
|
||||
/// </summary>
|
||||
/// <param name="val">The value to be used</param>
|
||||
void SpinBox::DoubleClickNonZero(int val)
|
||||
{
|
||||
m_DoubleClickNonZero = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the small step to be used when the user holds down shift while scrolling.
|
||||
/// The default is step / 10, so use this if something else is needed.
|
||||
/// </summary>
|
||||
/// <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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expose the underlying QLineEdit control to the caller.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to the QLineEdit</returns>
|
||||
QLineEdit* SpinBox::lineEdit()
|
||||
{
|
||||
return QSpinBox::lineEdit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Another workaround for the persistent text selection bug in Qt.
|
||||
/// </summary>
|
||||
void SpinBox::onSpinBoxValueChanged(int i)
|
||||
{
|
||||
lineEdit()->deselect();//Gets rid of nasty "feature" that always has text selected.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event filter for taking special action on double click events.
|
||||
/// </summary>
|
||||
/// <param name="o">The object</param>
|
||||
/// <param name="e">The eevent</param>
|
||||
/// <returns>false</returns>
|
||||
bool SpinBox::eventFilter(QObject* o, QEvent* e)
|
||||
{
|
||||
if (e->type() == QMouseEvent::MouseButtonPress && isEnabled())
|
||||
{
|
||||
QPoint pt;
|
||||
|
||||
if (QMouseEvent* me = (QMouseEvent*)e)
|
||||
pt = me->localPos().toPoint();
|
||||
|
||||
int pos = lineEdit()->cursorPositionAt(pt);
|
||||
|
||||
if (lineEdit()->selectedText() != "")
|
||||
{
|
||||
lineEdit()->deselect();
|
||||
lineEdit()->setCursorPosition(pos);
|
||||
return true;
|
||||
}
|
||||
else if (m_Select)
|
||||
{
|
||||
lineEdit()->setCursorPosition(pos);
|
||||
selectAll();
|
||||
m_Select = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m_DoubleClick && e->type() == QMouseEvent::MouseButtonDblClick && isEnabled())
|
||||
{
|
||||
if (value() == 0)
|
||||
setValue(m_DoubleClickZero);
|
||||
else
|
||||
setValue(m_DoubleClickNonZero);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e->type() == QEvent::Wheel)
|
||||
{
|
||||
//Take special action for shift to reduce the scroll amount. Control already
|
||||
//increases it automatically.
|
||||
if (QWheelEvent* wheelEvent = dynamic_cast<QWheelEvent*>(e))
|
||||
{
|
||||
Qt::KeyboardModifiers mod = wheelEvent->modifiers();
|
||||
|
||||
if (mod.testFlag(Qt::ShiftModifier))
|
||||
setSingleStep(m_SmallStep);
|
||||
else
|
||||
setSingleStep(m_Step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus enters the spinner.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void SpinBox::focusInEvent(QFocusEvent* e)
|
||||
{
|
||||
lineEdit()->setReadOnly(false);
|
||||
QSpinBox::focusInEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus leaves the spinner.
|
||||
/// Qt has a nasty "feature" that leaves the text in a spinner selected
|
||||
/// and the cursor visible, regardless of whether it has the focus.
|
||||
/// Manually clear both here.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void SpinBox::focusOutEvent(QFocusEvent* e)
|
||||
{
|
||||
lineEdit()->deselect();//Clear selection when leaving.
|
||||
lineEdit()->setReadOnly(true);//Clever hack to clear the cursor when leaving.
|
||||
QSpinBox::focusOutEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus enters the spinner.
|
||||
/// Must set the focus to make sure key down messages don't erroneously go to the GLWidget.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void SpinBox::enterEvent(QEvent* e)
|
||||
{
|
||||
m_Select = true;
|
||||
setFocus();
|
||||
QSpinBox::enterEvent(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when focus leaves the spinner.
|
||||
/// Must clear the focus to make sure key down messages don't erroneously go to the GLWidget.
|
||||
/// </summary>
|
||||
/// <param name="e">The event</param>
|
||||
void SpinBox::leaveEvent(QEvent* e)
|
||||
{
|
||||
m_Select = false;
|
||||
clearFocus();
|
||||
QSpinBox::leaveEvent(e);
|
||||
}
|
45
Source/Fractorium/SpinBox.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// SpinBox class.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// A derivation to prevent the spin box from selecting its own text
|
||||
/// when editing. Also to prevent multiple spin boxes from all having
|
||||
/// selected text at once.
|
||||
/// </summary>
|
||||
class SpinBox : public QSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SpinBox(QWidget* parent = 0, int height = 16, int step = 1);
|
||||
virtual ~SpinBox() { }
|
||||
void SetValueStealth(int d);
|
||||
void DoubleClick(bool b);
|
||||
void DoubleClickZero(int val);
|
||||
void DoubleClickNonZero(int val);
|
||||
void SmallStep(int step);
|
||||
QLineEdit* lineEdit();
|
||||
|
||||
public slots:
|
||||
void onSpinBoxValueChanged(int i);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* o, QEvent* e);
|
||||
virtual void focusInEvent(QFocusEvent* e);
|
||||
virtual void focusOutEvent(QFocusEvent* e);
|
||||
virtual void enterEvent(QEvent* e);
|
||||
virtual void leaveEvent(QEvent* e);
|
||||
|
||||
private:
|
||||
bool m_Select;
|
||||
bool m_DoubleClick;
|
||||
int m_DoubleClickNonZero;
|
||||
int m_DoubleClickZero;
|
||||
int m_Step;
|
||||
int m_SmallStep;
|
||||
};
|
30
Source/Fractorium/StealthComboBox.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// StealthComboBox class.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// A thin derivation of QComboBox which allows the user
|
||||
/// to set the index without triggering signals.
|
||||
/// </summary>
|
||||
class StealthComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StealthComboBox(QWidget* parent = 0) : QComboBox(parent) { }
|
||||
|
||||
/// <summary>
|
||||
/// Set the current index of the combo box without triggering signals.
|
||||
/// </summary>
|
||||
/// <param name="index">The current index to set</param>
|
||||
void StealthComboBox::SetCurrentIndexStealth(int index)
|
||||
{
|
||||
blockSignals(true);
|
||||
setCurrentIndex(index);
|
||||
blockSignals(false);
|
||||
}
|
||||
};
|
57
Source/Fractorium/TableWidget.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// TableWidget class.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// The entire purpose for this subclass is to overcome a glaring flaw
|
||||
/// in the way Qt handles table drawing.
|
||||
/// For most of the tables Fractorium uses, it draw the grid lines. Qt draws them
|
||||
/// in a very naive manner, whereby it draws lines above the first row and below
|
||||
/// the last row. It also draws to the left of the first column and to the right
|
||||
/// of the last column. This has the effect of putting an additional border inside
|
||||
/// of the specified border. This extra border becomes very noticeable when changing
|
||||
/// the background color of a cell.
|
||||
/// The workaround is to scrunch the size of the table up by one pixel. However,
|
||||
/// since the viewable area is then smaller than the size of the table, it will scroll
|
||||
/// by one pixel if the mouse is hovered over the table and the user moves the mouse wheel.
|
||||
/// This subclass is done solely to filter out the mouse wheel event.
|
||||
/// Note that this filtering only applies to the table as a whole, which means
|
||||
/// mouse wheel events still get properly routed to spinners.
|
||||
/// </summary>
|
||||
class TableWidget : public QTableWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that passes the parent to the base and installs
|
||||
/// the event filter.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
explicit TableWidget(QWidget* parent = 0)
|
||||
: QTableWidget(parent)
|
||||
{
|
||||
viewport()->installEventFilter(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Event filter to ignore mouse wheel events.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object sending the event</param>
|
||||
/// <param name="e">The event</param>
|
||||
/// <returns>True if mouse wheel, else return the result of calling the base fucntion.</returns>
|
||||
bool eventFilter(QObject* obj, QEvent* e)
|
||||
{
|
||||
if(e->type() == QEvent::Wheel)
|
||||
{
|
||||
e->ignore();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QTableWidget::eventFilter(obj, e);
|
||||
}
|
||||
};
|
2
Source/Fractorium/TwoButtonWidget.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "TwoButtonWidget.h"
|
113
Source/Fractorium/TwoButtonWidget.h
Normal file
@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "DoubleSpinBox.h"
|
||||
|
||||
/// <summary>
|
||||
/// TwoButtonWidget and SpinnerButtonWidget classes.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Thin container that is both a widget and a container of two QPushButtons.
|
||||
/// Used for when a layout expects a single widget, but two need to go in its place.
|
||||
/// The buttons are public so the caller can easily use them individually.
|
||||
/// </summary>
|
||||
class TwoButtonWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that passes the parent to the base, then creates two QPushButtons,
|
||||
/// and sets up their captions and dimensions.
|
||||
/// </summary>
|
||||
/// <param name="caption1">The caption of the first button</param>
|
||||
/// <param name="caption2">The caption of the second button</param>
|
||||
/// <param name="w1">The width of the first button</param>
|
||||
/// <param name="w2">The width of the second button</param>
|
||||
/// <param name="h">The height of both buttons</param>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
TwoButtonWidget(QString caption1, QString caption2, int w1, int w2, int h, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
m_Button1 = new QPushButton(caption1, parent);
|
||||
m_Button2 = new QPushButton(caption2, parent);
|
||||
|
||||
if (w1 != -1)
|
||||
{
|
||||
m_Button1->setMinimumWidth(w1);
|
||||
m_Button1->setMaximumWidth(w1);
|
||||
}
|
||||
|
||||
if (w2 != -1)
|
||||
{
|
||||
m_Button2->setMinimumWidth(w2);
|
||||
m_Button2->setMaximumWidth(w2);
|
||||
}
|
||||
|
||||
m_Button1->setMinimumHeight(h);
|
||||
m_Button1->setMaximumHeight(h);
|
||||
m_Button2->setMinimumHeight(h);
|
||||
m_Button2->setMaximumHeight(h);
|
||||
|
||||
layout->addWidget(m_Button1);
|
||||
layout->addWidget(m_Button2);
|
||||
layout->setAlignment(Qt::AlignLeft);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(2);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
QPushButton* m_Button1;
|
||||
QPushButton* m_Button2;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Thin container that is both a widget and a container of one DoubleSpinBox and one QPushButton.
|
||||
/// Used for when a layout expects a single widget, but two need to go in its place.
|
||||
/// The widgets are public so the caller can easily use them individually.
|
||||
/// </summary>
|
||||
class SpinnerButtonWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that passes the parent to the base, then creates a QPushButton and
|
||||
/// sets up its caption and dimensions, then assigns the DoubleSpinBox.
|
||||
/// </summary>
|
||||
/// <param name="spinBox">The pre-created DoubleSpinBox</param>
|
||||
/// <param name="buttonCaption">The caption of the button</param>
|
||||
/// <param name="w">The width of the button</param>
|
||||
/// <param name="h">The height of the button</param>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
SpinnerButtonWidget(DoubleSpinBox* spinBox, QString buttonCaption, int w, int h, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
m_Button = new QPushButton(buttonCaption, parent);
|
||||
m_SpinBox = spinBox;
|
||||
|
||||
if (w != -1)
|
||||
{
|
||||
m_Button->setMinimumWidth(w);
|
||||
m_Button->setMaximumWidth(w);
|
||||
}
|
||||
|
||||
m_Button->setMinimumHeight(h);
|
||||
m_Button->setMaximumHeight(h);
|
||||
|
||||
layout->addWidget(spinBox);
|
||||
layout->addWidget(m_Button);
|
||||
layout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
DoubleSpinBox* m_SpinBox;
|
||||
QPushButton* m_Button;
|
||||
};
|
89
Source/Fractorium/VariationTreeWidgetItem.h
Normal file
@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "DoubleSpinBox.h"
|
||||
|
||||
/// <summary>
|
||||
/// TwoButtonWidget class.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// A derivation of QTreeWidgetItem which helps us with sorting.
|
||||
/// This is used when the user chooses to sort the variations tree
|
||||
/// by index or by weight. It supports weights less than, equal to, or
|
||||
/// greater than zero.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class VariationTreeWidgetItem : public QTreeWidgetItem
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that takes a pointer to a QTreeWidget as the parent
|
||||
/// and passes it to the base.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
VariationTreeWidgetItem(QTreeWidget* parent = 0)
|
||||
: QTreeWidgetItem(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a pointer to a QTreeWidgetItem as the parent
|
||||
/// and passes it to the base.
|
||||
/// This is used for making sub items for parametric variation parameters.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent widget</param>
|
||||
VariationTreeWidgetItem(QTreeWidgetItem* parent = 0)
|
||||
: QTreeWidgetItem(parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~VariationTreeWidgetItem() { }
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Less than operator used for sorting.
|
||||
/// </summary>
|
||||
/// <param name="other">The QTreeWidgetItem to compare against for sorting</param>
|
||||
/// <returns>True if this is less than other, else false.</returns>
|
||||
bool operator < (const QTreeWidgetItem& other) const
|
||||
{
|
||||
int column = treeWidget()->sortColumn();
|
||||
eVariationId index1, index2;
|
||||
double weight1 = 0, weight2 = 0;
|
||||
VariationTreeWidgetItem<T>* varItemWidget;
|
||||
VariationTreeDoubleSpinBox<T>* spinBox1, *spinBox2;
|
||||
|
||||
QWidget* itemWidget1 = treeWidget()->itemWidget(const_cast<VariationTreeWidgetItem<T>*>(this), 1);//Get the widget for the second column.
|
||||
|
||||
if (spinBox1 = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(itemWidget1))//Cast the widget to the VariationTreeDoubleSpinBox type.
|
||||
{
|
||||
QWidget* itemWidget2 = treeWidget()->itemWidget(const_cast<QTreeWidgetItem*>(&other), 1);//Get the widget for the second column of the widget item passed in.
|
||||
|
||||
if (spinBox2 = dynamic_cast<VariationTreeDoubleSpinBox<T>*>(itemWidget2))//Cast the widget to the VariationTreeDoubleSpinBox type.
|
||||
{
|
||||
if (spinBox1->IsParam() || spinBox2->IsParam())//Do not sort params, their order will always remain the same.
|
||||
return false;
|
||||
|
||||
weight1 = spinBox1->value();
|
||||
weight2 = spinBox2->value();
|
||||
index1 = spinBox1->GetVariation()->VariationId();
|
||||
index2 = spinBox2->GetVariation()->VariationId();
|
||||
|
||||
if (column == 0)//First column clicked, sort by variation index.
|
||||
{
|
||||
return index1 < index2;
|
||||
}
|
||||
else if (column == 1)//Second column clicked, sort by weight.
|
||||
{
|
||||
if (IsNearZero(weight1) && IsNearZero(weight2))
|
||||
return index1 > index2;
|
||||
else
|
||||
return fabs(weight1) < fabs(weight2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
17
Source/Fractorium/resource.h
Normal file
@ -0,0 +1,17 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Fractorium.rc
|
||||
//
|
||||
#define IDI_ICON1 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NO_MFC 1
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|