--User changes

-Add new preset dimensions to the right click menu of the width and height fields in the editor.
-Change QSS stylesheets to properly handle tabs.
-Make tabs rectangular by default. For some reason, they had always been triangular.

--Bug fixes
 -Incremental rendering times in the editor were wrong.

--Code changes
 -Migrate to Qt6. There is probably more work to be done here.
-Migrate to VS2022.
-Migrate to Wix 4 installer.
-Change installer to install to program files for all users.
-Fix many VS2022 code analysis warnings.
-No longer use byte typedef, because std::byte is now a type. Revert all back to unsigned char.
-Upgrade OpenCL headers to version 3.0 and keep locally now rather than trying to look for system files.
-No longer link to Nvidia or AMD specific OpenCL libraries. Use the generic installer located at OCL_ROOT too.
-Add the ability to change OpenCL grid dimensions. This was attempted for investigating possible performance improvments, but made no difference.

This has not been verified on Linux or Mac yet.
This commit is contained in:
Person
2023-04-25 17:59:54 -06:00
parent 64d4470b12
commit 1dfbd4eff2
306 changed files with 514515 additions and 491207 deletions

View File

@ -1,21 +1,21 @@
#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="p">The parent widget. Default: nullptr.</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumAboutDialog::FractoriumAboutDialog(QWidget* p, Qt::WindowFlags f)
: QDialog(p, f)
{
ui.setupUi(this);
adjustSize();//Must do this to ensure all text is displayed when using different fonts.
setMinimumHeight(height());//Once properly sized, disallow resizing.
setMaximumHeight(height());
setMinimumWidth(width());
setMaximumWidth(width());
ui.CreditsTextBrowser->setOpenLinks(true);
ui.CreditsTextBrowser->setOpenExternalLinks(true);
}
#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="p">The parent widget. Default: nullptr.</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumAboutDialog::FractoriumAboutDialog(QWidget* p, Qt::WindowFlags f)
: QDialog(p, f)
{
ui.setupUi(this);
adjustSize();//Must do this to ensure all text is displayed when using different fonts.
setMinimumHeight(height());//Once properly sized, disallow resizing.
setMaximumHeight(height());
setMinimumWidth(width());
setMaximumWidth(width());
ui.CreditsTextBrowser->setOpenLinks(true);
ui.CreditsTextBrowser->setOpenExternalLinks(true);
}

View File

@ -1,22 +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* p = nullptr, Qt::WindowFlags f = 0);
private:
Ui::AboutDialog ui;
};
#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* p = nullptr, Qt::WindowFlags f = Qt::WindowType::Widget);
private:
Ui::AboutDialog ui;
};

View File

@ -1,198 +1,198 @@
<?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>692</width>
<height>729</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>546</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>About</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<widget class="QLabel" name="DescriptionLabel">
<property name="geometry">
<rect>
<x>6</x>
<y>5</y>
<width>681</width>
<height>171</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Version format:&lt;/p&gt;&lt;p&gt;[TOTAL RELEASE COUNT].&lt;/p&gt;&lt;p&gt;[2 DIGIT YEAR OF RELEASE].&lt;/p&gt;&lt;p&gt;[MONTH OF RELEASE].&lt;/p&gt;&lt;p&gt;[TOTAL RELEASE COUNT FOR THE YEAR]&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Fractorium 22.21.4.2&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
<property name="margin">
<number>1</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>190</y>
<width>671</width>
<height>501</height>
</rect>
</property>
<layout class="QVBoxLayout" name="AboutVerticalLayout">
<item>
<widget class="QTextBrowser" name="CreditsTextBrowser">
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.14286pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Developers:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Lead: &lt;/span&gt;&lt;a href=&quot;http://www.fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Matt Feemster&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br /&gt;Contributors: &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/triptychaos&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Michel Mastriani,&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; Simon Detheridge&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Builders, Testers and Advisors:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Linux deployment: &lt;/span&gt;&lt;a href=&quot;https://launchpad.net/~fractorium&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Gambhiro Bhikkhu&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Testing and Feedback: &lt;/span&gt;&lt;a href=&quot;http://snicker02.deviantart.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Brad Stefanov&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.fxysw.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Fengda&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.deviantart.com/b33rheart&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Gabor Timar&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/guephren&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Guephren&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/jeddaka&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Jeddaka,&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; &lt;/span&gt;&lt;a href=&quot;http://boosthardware.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Patrick Shirkey&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://nightmaretf.deviantart.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Richard Vollebregt&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, Simon Detheridge, Tai,&lt;/span&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; &lt;/span&gt;&lt;a href=&quot;http://gameryamen.deviantart.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Yamen&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Code and theory questions: &lt;/span&gt;&lt;a href=&quot;https://github.com/scottdraves/flam3&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Erik Reckase&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://fractron9000.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Mike Thiesen&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://github.com/scottdraves/flam3&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Scott Draves&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://github.com/stevenrobertson/cuburn&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Steve Robertson&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.chaoticafractals.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Thomas Ludwig&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Code Copied:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;https://github.com/scottdraves/flam3&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;flam3&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Scott Draves, Erik Reckase (GPL v2)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://github.com/stevenrobertson/cuburn&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;cuburn&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Steven Robertson, Michael Semeniuk, Matthew Znoj, Nicolas Mejia (GPL v3)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://fractron9000.sourceforge.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Fractron 9000&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Mike Thiesen (GPL)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://sourceforge.net/projects/apophysis7x&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Apophysis&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Mark Townsend, Ronald Hordijk, Peter Sdobnov, Piotr Borys, Georg Kiehne (GPL)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://jwildfire.org/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;JWildfire&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Andreas Maschke (LGPL)&lt;br /&gt;gradLib: Stian Broen&lt;br /&gt;ColorPickerWidget: Etienne Moutot&lt;br /&gt;Numerous Apophysis plugin developers (GPL)&lt;br /&gt;Chaotica variations: &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tatasz&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Tatasz&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.chaoticafractals.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Thomas Ludwig&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:7.875pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Libraries Linked:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://www.qt.io/developers/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Qt&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Digia Plc (GPL v3, LGPL v2)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://g-truc.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;glm&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Christophe Riccio (MIT License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://threadingbuildingblocks.org&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Threading Building Blocks&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Intel Corporation (GPLv2)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://libjpeg.sourceforge.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;libjpeg&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Independent JPEG Group (Free Software License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://libpng.org&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;libpng&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Glenn Randers-Pehrson et al (Libpng License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://xmlsoft.org&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;libxml2&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Daniel Veillard (MIT License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://zlib.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;zlib&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Jean-loup Gailly, Mark Adler (Zlib License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://burtleburtle.net/bob/cplus/isaac.hpp&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;QTIsaac&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Robert J. Jenkins, Quinn Tyler Jackson (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://cas.ee.ic.ac.uk/people/dt10/research/rngs-gpu-mwc64x.html&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;MWC64X Random Number Generator&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: David Thomas (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/brofield/simpleopt&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;SimpleOpt&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Brodie Thiesfield (MIT License)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Icons and Palettes Used:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Palettes: &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/boxtail&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Boxtail&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/fardareismai&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;FarDareisMai&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/fractaldesire&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;FractalDesire&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, Rce, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/rubydeva&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Rubydeva&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tatasz&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Tatasz&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://famfamfam.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Silk&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Mark James (Creative Commons Attribution 2.5 License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://momentumdesignlab.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Momentum&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Momentum Design Lab (Creative Commons Attribution-ShareAlike 3.5 License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://everaldo.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Crystal Clear&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Everaldo Coelho (LGPL)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://openiconlibrary.sourceforge.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Open Icon Library&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Jeff Israel (GPL, LGPL, Creative Commons, Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://icons.mysitemyway.com/category/3d-transparent-glass-icons/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;3D Transparent Glass&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: iconsETC (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://p.yusukekamiyamane.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Fugue&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Yusuke Kamiyamane (Creative Commons Attribution 3.0 License)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Benchmark and Example Flames:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;https://www.deviantart.com/c-91&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;C-91&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/b33rheart&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Gabor Timar&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/golubaja&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Golubaja&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/pillemaster&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Pillemaster&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/plangkye&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Plangkye&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tatasz&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Tatasz&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/triptychaos&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Triptychaos&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tyrantwave&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;TyrantWave&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/zy0rg&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Zy0rg&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="openLinks">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>5</x>
<y>695</y>
<width>681</width>
<height>30</height>
</rect>
</property>
<layout class="QGridLayout" name="AboutOkGridLayout" rowminimumheight="0">
<property name="verticalSpacing">
<number>6</number>
</property>
<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>
</widget>
</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>
<?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>692</width>
<height>729</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>546</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>About</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<widget class="QLabel" name="DescriptionLabel">
<property name="geometry">
<rect>
<x>6</x>
<y>5</y>
<width>681</width>
<height>171</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Version format:&lt;/p&gt;&lt;p&gt;[TOTAL RELEASE COUNT].&lt;/p&gt;&lt;p&gt;[2 DIGIT YEAR OF RELEASE].&lt;/p&gt;&lt;p&gt;[MONTH OF RELEASE].&lt;/p&gt;&lt;p&gt;[TOTAL RELEASE COUNT FOR THE YEAR]&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Fractorium 22.21.4.2&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
<property name="margin">
<number>1</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>190</y>
<width>671</width>
<height>501</height>
</rect>
</property>
<layout class="QVBoxLayout" name="AboutVerticalLayout">
<item>
<widget class="QTextBrowser" name="CreditsTextBrowser">
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.14286pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Developers:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Lead: &lt;/span&gt;&lt;a href=&quot;http://www.fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Matt Feemster&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br /&gt;Contributors: &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/triptychaos&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Michel Mastriani,&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; Simon Detheridge&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Builders, Testers and Advisors:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Linux deployment: &lt;/span&gt;&lt;a href=&quot;https://launchpad.net/~fractorium&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Gambhiro Bhikkhu&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Testing and Feedback: &lt;/span&gt;&lt;a href=&quot;http://snicker02.deviantart.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Brad Stefanov&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.fxysw.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Fengda&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.deviantart.com/b33rheart&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Gabor Timar&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/guephren&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Guephren&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/jeddaka&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Jeddaka,&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; &lt;/span&gt;&lt;a href=&quot;http://boosthardware.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Patrick Shirkey&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://nightmaretf.deviantart.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Richard Vollebregt&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, Simon Detheridge, Tai,&lt;/span&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; &lt;/span&gt;&lt;a href=&quot;http://gameryamen.deviantart.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Yamen&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Code and theory questions: &lt;/span&gt;&lt;a href=&quot;https://github.com/scottdraves/flam3&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Erik Reckase&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://fractron9000.sourceforge.net/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Mike Thiesen&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://github.com/scottdraves/flam3&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Scott Draves&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://github.com/stevenrobertson/cuburn&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Steve Robertson&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.chaoticafractals.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Thomas Ludwig&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Code Copied:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;https://github.com/scottdraves/flam3&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;flam3&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Scott Draves, Erik Reckase (GPL v2)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://github.com/stevenrobertson/cuburn&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;cuburn&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Steven Robertson, Michael Semeniuk, Matthew Znoj, Nicolas Mejia (GPL v3)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://fractron9000.sourceforge.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Fractron 9000&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Mike Thiesen (GPL)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://sourceforge.net/projects/apophysis7x&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Apophysis&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Mark Townsend, Ronald Hordijk, Peter Sdobnov, Piotr Borys, Georg Kiehne (GPL)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://jwildfire.org/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;JWildfire&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Andreas Maschke (LGPL)&lt;br /&gt;gradLib: Stian Broen&lt;br /&gt;ColorPickerWidget: Etienne Moutot&lt;br /&gt;Numerous Apophysis plugin developers (GPL)&lt;br /&gt;Chaotica variations: &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tatasz&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Tatasz&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;http://www.chaoticafractals.com/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Thomas Ludwig&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:7.875pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Libraries Linked:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://www.qt.io/developers/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Qt&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Digia Plc (GPL v3, LGPL v2)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://g-truc.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;glm&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Christophe Riccio (MIT License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://threadingbuildingblocks.org&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Threading Building Blocks&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Intel Corporation (GPLv2)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://libjpeg.sourceforge.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;libjpeg&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Independent JPEG Group (Free Software License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://libpng.org&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;libpng&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Glenn Randers-Pehrson et al (Libpng License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://xmlsoft.org&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;libxml2&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Daniel Veillard (MIT License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://zlib.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;zlib&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Jean-loup Gailly, Mark Adler (Zlib License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://burtleburtle.net/bob/cplus/isaac.hpp&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;QTIsaac&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Robert J. Jenkins, Quinn Tyler Jackson (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://cas.ee.ic.ac.uk/people/dt10/research/rngs-gpu-mwc64x.html&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;MWC64X Random Number Generator&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: David Thomas (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/brofield/simpleopt&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;SimpleOpt&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Brodie Thiesfield (MIT License)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Icons and Palettes Used:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Palettes: &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/boxtail&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Boxtail&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/fardareismai&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;FarDareisMai&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/fractaldesire&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;FractalDesire&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, Rce, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/rubydeva&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Rubydeva&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tatasz&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Tatasz&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://famfamfam.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Silk&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Mark James (Creative Commons Attribution 2.5 License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://momentumdesignlab.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Momentum&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Momentum Design Lab (Creative Commons Attribution-ShareAlike 3.5 License)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://everaldo.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Crystal Clear&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Everaldo Coelho (LGPL)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://openiconlibrary.sourceforge.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Open Icon Library&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Jeff Israel (GPL, LGPL, Creative Commons, Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://icons.mysitemyway.com/category/3d-transparent-glass-icons/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;3D Transparent Glass&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: iconsETC (Public Domain)&lt;br /&gt;&lt;/span&gt;&lt;a href=&quot;http://p.yusukekamiyamane.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Fugue&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;: Yusuke Kamiyamane (Creative Commons Attribution 3.0 License)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;Benchmark and Example Flames:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;https://www.deviantart.com/c-91&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;C-91&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/b33rheart&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Gabor Timar&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/golubaja&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Golubaja&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/pillemaster&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Pillemaster&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/plangkye&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Plangkye&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tatasz&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Tatasz&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/triptychaos&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Triptychaos&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/tyrantwave&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;TyrantWave&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, &lt;/span&gt;&lt;a href=&quot;https://www.deviantart.com/zy0rg&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Zy0rg&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="openLinks">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>5</x>
<y>695</y>
<width>681</width>
<height>30</height>
</rect>
</property>
<layout class="QGridLayout" name="AboutOkGridLayout" rowminimumheight="0">
<property name="verticalSpacing">
<number>6</number>
</property>
<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>
</widget>
</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>

View File

@ -1,276 +1,282 @@
#include "FractoriumPch.h"
#include "CurvesGraphicsView.h"
/// <summary>
/// Construct the scene which will have a fixed rect.
/// Construct all points, pens and axes.
/// </summary>
/// <param name="parent">Pass to the parent</param>
CurvesGraphicsView::CurvesGraphicsView(QWidget* parent)
: QGraphicsView(parent)
{
m_Scene.setSceneRect(0, 0, 245, 245);
m_AxisPen = QPen(Qt::GlobalColor::white);
m_APen = QPen(Qt::GlobalColor::black); m_Pens[0] = &m_APen;
m_RPen = QPen(Qt::GlobalColor::red); m_Pens[1] = &m_RPen;
m_GPen = QPen(Qt::GlobalColor::green); m_Pens[2] = &m_GPen;
m_BPen = QPen(Qt::GlobalColor::blue); m_Pens[3] = &m_BPen;
m_APen.setWidth(2);
m_RPen.setWidth(2);
m_GPen.setWidth(2);
m_BPen.setWidth(2);
setScene(&m_Scene);
//qDebug() << "Original scene rect before setting anything is: " << sceneRect();
m_OriginalRect = sceneRect();
Curves<float> curves(true);
Set(curves);
show();
//qDebug() << "Original scene rect is: " << m_OriginalRect;
}
/// <summary>
/// Called when an underlying point has had its position changed, so emit a signal so that a listener can take action.
/// </summary>
/// <param name="curveIndex">The curve whose point value was changed, 0-3.</param>
/// <param name="pointIndex">The point within the curve whose point value was changed.</param>
/// <param name="point">The position of the point. X,Y will each be within 0-1.</param>
void CurvesGraphicsView::PointChanged(int curveIndex, int pointIndex, const QPointF& point)
{
if (curveIndex == m_Index)
{
double x = point.x() / width();
double y = (height() - point.y()) / height();
emit PointChangedSignal(curveIndex, pointIndex, QPointF(x, y));
}
}
/// <summary>
/// Get the position of a given point within a given curve.
/// </summary>
/// <param name="curveIndex">The curve whose point value will be retrieved, 0-3.</param>
/// <param name="pointIndex">The point within the curve whose value will be retrieved, 0-3.</param>
/// <returns>The position of the point. X,Y will each be within 0-1.</returns>
QPointF CurvesGraphicsView::Get(int curveIndex, int pointIndex)
{
if (curveIndex < 4 && pointIndex < m_Points[curveIndex].size())
{
EllipseItem* item = m_Points[curveIndex][pointIndex];
return QPointF(item->pos().x() / width(), (height() - item->pos().y()) / height());
}
return QPointF();
}
/// <summary>
/// Set the position of a given point within a given curve.
/// </summary>
/// <param name="curveIndex">The curve whose point will be set, 0-3.</param>
/// <param name="pointIndex">The point within the curve which will be set, 0-3</param>
/// <param name="point">The position to set the point to. X,Y will each be within 0-1.</param>
void CurvesGraphicsView::Set(int curveIndex, int pointIndex, const QPointF& point)
{
if (curveIndex < 4 && pointIndex < m_Points[curveIndex].size())
{
m_Points[curveIndex][pointIndex]->setPos(point.x() * width(), (1.0 - point.y()) * height());//Scale to scene dimensions, Y axis is flipped.
}
}
void CurvesGraphicsView::Set(Curves<float>& curves)
{
m_Scene.clear();
m_XLine = new QGraphicsLineItem();
m_XLine->setPen(m_AxisPen);
m_XLine->setZValue(0);
m_YLine = new QGraphicsLineItem();
m_YLine->setPen(m_AxisPen);
m_YLine->setZValue(0);
m_Scene.addItem(m_XLine);
m_Scene.addItem(m_YLine);
auto createpoints = [&](int index, vector<EllipseItem*>& items, Qt::GlobalColor col, int zval)
{
items.clear();
m_Points[index].clear();
for (int i = 0; i < curves.m_Points[index].size(); i++)
{
auto item = new EllipseItem(QRectF(-5, -5, 10, 10), index, i, this);
items.push_back(item);
item->setBrush(QBrush(col));
m_Scene.addItem(item);
m_Points[index].push_back(item);
item->setZValue(zval);
QPointF point(curves.m_Points[index][i].x, curves.m_Points[index][i].y);
Set(index, i, point);
}
};
createpoints(0, m_AllP, Qt::GlobalColor::black, 2);
createpoints(1, m_RedP, Qt::GlobalColor::red, 1);
createpoints(2, m_GrnP, Qt::GlobalColor::green, 1);
createpoints(3, m_BluP, Qt::GlobalColor::blue, 1);
SetTop(CurveIndex(m_Index));
}
/// <summary>
/// Set the topmost curve but setting its Z value.
/// All other curves will get a value one less.
/// </summary>
/// <param name="curveIndex">The curve to set</param>
void CurvesGraphicsView::SetTop(CurveIndex curveIndex)
{
switch (curveIndex)
{
case CurveIndex::ALL:
m_Index = 0;
break;
case CurveIndex::RED:
m_Index = 1;
break;
case CurveIndex::GREEN:
m_Index = 2;
break;
case CurveIndex::BLUE:
default:
m_Index = 3;
}
for (size_t i = 0; i < 4; i++)
{
bool b = (i == m_Index);
for (auto& p : m_Points[i])
p->SetCurrent(b);
}
}
/// <summary>
/// Overridden paint even which draws the points, axes and curves.
/// </summary>
/// <param name="e">Ignored</param>
void CurvesGraphicsView::paintEvent(QPaintEvent* e)
{
QGraphicsView::paintEvent(e);
int i;
QRectF rect = scene()->sceneRect();
double w2 = width() / 2;
double h2 = height() / 2;
//Draw axis lines.
m_XLine->setLine(QLineF(0, h2, width(), h2));
m_YLine->setLine(QLineF(w2, 0, w2, height()));
//This must be constructed every time and cannot be a member.
QPainter painter(viewport());
painter.setClipRect(rect);
painter.setRenderHint(QPainter::Antialiasing);
auto points = m_Points;
for (auto& p : points)
{
if (p.size() < 2)
return;
std::sort(p.begin(), p.end(), [&](auto & lhs, auto & rhs) { return lhs->pos().x() < rhs->pos().x(); });
}
//Create 4 new paths. These must be constructed every time and cannot be members.
//Need to sort the points here first based on their x coordinate.
QPainterPath paths[4] =
{
QPainterPath(points[0][0]->pos()),
QPainterPath(points[1][0]->pos()),
QPainterPath(points[2][0]->pos()),
QPainterPath(points[3][0]->pos())
};
int topmost = 0;
//Construct paths or all curves, and draw them for all but the topmost curve.
for (i = 0; i < 4; i++)
{
vector<v2F> vals;
vals.reserve(points[i].size());
for (auto& p : points[i])
vals.push_back({ p->pos().x(), p->pos().y() });
Spline<float> spline(vals);
for (int j = 0; j < rect.width(); j++)
{
auto x = j;
auto y = spline.Interpolate(x);
paths[i].lineTo(QPointF(x, y));
}
if (points[i][0]->zValue() == 1)
{
painter.setPen(*m_Pens[i]);
painter.drawPath(paths[i]);
}
else
topmost = i;
}
//Draw the topmost curve.
painter.setPen(*m_Pens[topmost]);
painter.drawPath(paths[topmost]);
}
void CurvesGraphicsView::mousePressEvent(QMouseEvent* e)
{
QGraphicsView::mousePressEvent(e);
auto thresh = devicePixelRatioF() * 4;
auto findpoint = [&](int x, int y, double thresh) -> int
{
for (int i = 0; i < m_Points[m_Index].size(); i++)
{
auto item = m_Points[m_Index][i];
auto xdist = std::abs(item->pos().x() - x);
auto ydist = std::abs(item->pos().y() - y);
auto threshAgain = thresh;
if (xdist < threshAgain && ydist < threshAgain)
return i;
}
return -1;
};
if (e->button() == Qt::RightButton)
{
int i = findpoint(e->pos().x(), e->pos().y(), thresh);
if (i != -1)
emit PointRemovedSignal(m_Index, i);
}
else if (findpoint(e->pos().x(), e->pos().y(), thresh * 2) == -1)
{
QRectF rect = scene()->sceneRect();
auto points = m_Points[m_Index];
if (points.size() < 2)
return;
std::sort(points.begin(), points.end(), [&](auto & lhs, auto & rhs) { return lhs->pos().x() < rhs->pos().x(); });
vector<v2F> vals;
vals.reserve(points.size());
for (auto& p : points)
vals.push_back({ p->pos().x(), p->pos().y() });
Spline<float> spline(vals);
for (int j = 0; j < rect.width(); j++)
{
auto y = spline.Interpolate(j);
auto xdist = std::abs(j - e->pos().x());
auto ydist = std::abs(y - e->pos().y());
if (xdist < thresh && ydist < thresh)
{
double x = e->pos().x() / (double)width();
double y = (height() - e->pos().y()) / (double)height();
emit PointAddedSignal(m_Index, QPointF(x, y));
break;
}
}
}
}
#include "FractoriumPch.h"
#include "CurvesGraphicsView.h"
/// <summary>
/// Construct the scene which will have a fixed rect.
/// Construct all points, pens and axes.
/// </summary>
/// <param name="parent">Pass to the parent</param>
CurvesGraphicsView::CurvesGraphicsView(QWidget* parent)
: QGraphicsView(parent)
{
m_Scene.setSceneRect(0, 0, 245, 245);
m_AxisPen = QPen(Qt::GlobalColor::white);
m_APen = QPen(Qt::GlobalColor::black); m_Pens[0] = &m_APen;
m_RPen = QPen(Qt::GlobalColor::red); m_Pens[1] = &m_RPen;
m_GPen = QPen(Qt::GlobalColor::green); m_Pens[2] = &m_GPen;
m_BPen = QPen(Qt::GlobalColor::blue); m_Pens[3] = &m_BPen;
m_APen.setWidth(2);
m_RPen.setWidth(2);
m_GPen.setWidth(2);
m_BPen.setWidth(2);
setScene(&m_Scene);
//qDebug() << "Original scene rect before setting anything is: " << sceneRect();
m_OriginalRect = sceneRect();
Curves<float> curves(true);
Set(curves);
show();
//qDebug() << "Original scene rect is: " << m_OriginalRect;
}
/// <summary>
/// Called when an underlying point has had its position changed, so emit a signal so that a listener can take action.
/// </summary>
/// <param name="curveIndex">The curve whose point value was changed, 0-3.</param>
/// <param name="pointIndex">The point within the curve whose point value was changed.</param>
/// <param name="point">The position of the point. X,Y will each be within 0-1.</param>
void CurvesGraphicsView::PointChanged(int curveIndex, int pointIndex, const QPointF& point)
{
if (curveIndex == m_Index)
{
const auto x = point.x() / width();
const auto y = (height() - point.y()) / height();
emit PointChangedSignal(curveIndex, pointIndex, QPointF(x, y));
}
}
/// <summary>
/// Get the position of a given point within a given curve.
/// </summary>
/// <param name="curveIndex">The curve whose point value will be retrieved, 0-3.</param>
/// <param name="pointIndex">The point within the curve whose value will be retrieved, 0-3.</param>
/// <returns>The position of the point. X,Y will each be within 0-1.</returns>
QPointF CurvesGraphicsView::Get(int curveIndex, int pointIndex)
{
if (curveIndex < 4 && pointIndex < m_Points[curveIndex].size())
{
if (EllipseItem* item = m_Points[curveIndex][pointIndex])
return QPointF(item->pos().x() / width(), (height() - item->pos().y()) / height());
}
return QPointF();
}
/// <summary>
/// Set the position of a given point within a given curve.
/// </summary>
/// <param name="curveIndex">The curve whose point will be set, 0-3.</param>
/// <param name="pointIndex">The point within the curve which will be set, 0-3</param>
/// <param name="point">The position to set the point to. X,Y will each be within 0-1.</param>
void CurvesGraphicsView::Set(int curveIndex, int pointIndex, const QPointF& point)
{
if (curveIndex < 4 && pointIndex < m_Points[curveIndex].size())
{
m_Points[curveIndex][pointIndex]->setPos(point.x() * width(), (1.0 - point.y()) * height());//Scale to scene dimensions, Y axis is flipped.
}
}
void CurvesGraphicsView::Set(Curves<float>& curves)
{
m_Scene.clear();
m_XLine = new QGraphicsLineItem();
m_XLine->setPen(m_AxisPen);
m_XLine->setZValue(0);
m_YLine = new QGraphicsLineItem();
m_YLine->setPen(m_AxisPen);
m_YLine->setZValue(0);
m_Scene.addItem(m_XLine);
m_Scene.addItem(m_YLine);
auto createpoints = [&](int index, vector<EllipseItem*>& items, Qt::GlobalColor col, int zval)
{
items.clear();
m_Points[index].clear();
for (int i = 0; i < curves.m_Points[index].size(); i++)
{
auto item = new EllipseItem(QRectF(-5, -5, 10, 10), index, i, this);
items.push_back(item);
item->setBrush(QBrush(col));
m_Scene.addItem(item);
m_Points[index].push_back(item);
item->setZValue(zval);
const QPointF point(curves.m_Points[index][i].x, curves.m_Points[index][i].y);
Set(index, i, point);
}
};
createpoints(0, m_AllP, Qt::GlobalColor::black, 2);
createpoints(1, m_RedP, Qt::GlobalColor::red, 1);
createpoints(2, m_GrnP, Qt::GlobalColor::green, 1);
createpoints(3, m_BluP, Qt::GlobalColor::blue, 1);
SetTop(CurveIndex(m_Index));
}
/// <summary>
/// Set the topmost curve but setting its Z value.
/// All other curves will get a value one less.
/// </summary>
/// <param name="curveIndex">The curve to set</param>
void CurvesGraphicsView::SetTop(CurveIndex curveIndex)
{
switch (curveIndex)
{
case CurveIndex::ALL:
m_Index = 0;
break;
case CurveIndex::RED:
m_Index = 1;
break;
case CurveIndex::GREEN:
m_Index = 2;
break;
case CurveIndex::BLUE:
default:
m_Index = 3;
}
for (size_t i = 0; i < 4; i++)
{
const auto b = (i == m_Index);
for (auto& p : m_Points[i])
p->SetCurrent(b);
}
}
/// <summary>
/// Overridden paint even which draws the points, axes and curves.
/// </summary>
/// <param name="e">Ignored</param>
void CurvesGraphicsView::paintEvent(QPaintEvent* e)
{
QGraphicsView::paintEvent(e);
int i;
const QRectF rect = scene()->sceneRect();
const double w2 = width() / 2;
const double h2 = height() / 2;
//Draw axis lines.
m_XLine->setLine(QLineF(0, h2, width(), h2));
m_YLine->setLine(QLineF(w2, 0, w2, height()));
//This must be constructed every time and cannot be a member.
QPainter painter(viewport());
painter.setClipRect(rect);
painter.setRenderHint(QPainter::Antialiasing);
auto points = m_Points;
for (auto& p : points)
{
if (p.size() < 2)
return;
std::sort(p.begin(), p.end(), [&](auto & lhs, auto & rhs) { return lhs->pos().x() < rhs->pos().x(); });
}
//Create 4 new paths. These must be constructed every time and cannot be members.
//Need to sort the points here first based on their x coordinate.
QPainterPath paths[4] =
{
QPainterPath(points[0][0]->pos()),
QPainterPath(points[1][0]->pos()),
QPainterPath(points[2][0]->pos()),
QPainterPath(points[3][0]->pos())
};
int topmost = 0;
//Construct paths or all curves, and draw them for all but the topmost curve.
for (i = 0; i < 4; i++)
{
vector<v2F> vals;
vals.reserve(points[i].size());
for (auto& p : points[i])
vals.push_back({ p->pos().x(), p->pos().y() });
Spline<float> spline(vals);
for (int j = 0; j < rect.width(); j++)
{
const auto x = j;
const auto y = spline.Interpolate(x);
paths[i].lineTo(QPointF(x, y));
}
if (points[i][0]->zValue() == 1)
{
painter.setPen(*m_Pens[i]);
painter.drawPath(paths[i]);
}
else
topmost = i;
}
//Draw the topmost curve.
painter.setPen(*m_Pens[topmost]);
painter.drawPath(paths[topmost]);
}
void CurvesGraphicsView::mousePressEvent(QMouseEvent* e)
{
QGraphicsView::mousePressEvent(e);
if (e != nullptr)
return;
const auto thresh = devicePixelRatioF() * 4;
auto findpoint = [&](int x, int y, double thresh) -> int
{
for (int i = 0; i < m_Points[m_Index].size(); i++)
{
if (auto item = m_Points[m_Index][i])
{
const auto xdist = std::abs(item->pos().x() - x);
const auto ydist = std::abs(item->pos().y() - y);
const auto threshAgain = thresh;
if (xdist < threshAgain && ydist < threshAgain)
return i;
}
}
return -1;
};
if (e->button() == Qt::RightButton)
{
const auto i = findpoint(e->pos().x(), e->pos().y(), thresh);
if (i != -1)
emit PointRemovedSignal(m_Index, i);
}
else if (findpoint(e->pos().x(), e->pos().y(), thresh * 2) == -1)
{
const auto rect = scene()->sceneRect();
auto points = m_Points[m_Index];
if (points.size() < 2)
return;
std::sort(points.begin(), points.end(), [&](auto & lhs, auto & rhs) { return lhs->pos().x() < rhs->pos().x(); });
vector<v2F> vals;
vals.reserve(points.size());
for (auto& p : points)
vals.push_back({ p->pos().x(), p->pos().y() });
Spline<float> spline(vals);
for (int j = 0; j < rect.width(); j++)
{
auto y = spline.Interpolate(j);
const auto xdist = std::abs(j - e->pos().x());
const auto ydist = std::abs(y - e->pos().y());
if (xdist < thresh && ydist < thresh)
{
const auto x = e->pos().x() / (double)width();
const auto y2 = (height() - e->pos().y()) / (double)height();
emit PointAddedSignal(m_Index, QPointF(x, y2));
break;
}
}
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,146 +1,146 @@
#pragma once
#include "FractoriumPch.h"
#include "FractoriumSettings.h"
/// <summary>
/// DoubleSpinBox and VariationTreeDoubleSpinBox classes.
/// </summary>
enum class eSpinToggle : et { NONE = 0, SPIN_DOUBLE_CLICK = 1, SPIN_RIGHT_CLICK = 2 };
/// <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 = nullptr, int height = 16, double step = 0.05, bool clearsel = true);
virtual ~DoubleSpinBox() { }
void SetValueStealth(double d);
void DoubleClick(bool b);
void DoubleClickLowVal(double val);
void DoubleClickZero(double val);
void DoubleClickNonZero(double val);
double Step();
void Step(double step);
double SmallStep();
void SmallStep(double step);
QLineEdit* lineEdit();
public slots:
void OnSpinBoxValueChanged(double d);
void OnTimeout();
protected:
virtual bool eventFilter(QObject* o, QEvent* e) override;
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void focusInEvent(QFocusEvent* e) override;
virtual void focusOutEvent(QFocusEvent* e) override;
virtual void enterEvent(QEvent* e) override;
virtual void leaveEvent(QEvent* e) override;
bool m_DoubleClick;
bool m_ClearSel;
shared_ptr<FractoriumSettings> m_Settings;
private:
void StartTimer();
void StopTimer();
double m_DoubleClickLowVal;
double m_DoubleClickNonZero;
double m_DoubleClickZero;
double m_Step;
double m_SmallStep;
QPoint m_MouseDownPoint;
QPoint m_MouseMovePoint;
static QTimer s_Timer;
};
/// <summary>
/// Thin derivation to implement the eventFilter() override which subsequently derived
/// classes will use to suppress showing the context menu when right clicking is used for toggling,
/// unless shift is pressed.
/// </summary>
class SpecialDoubleSpinBox : public DoubleSpinBox
{
Q_OBJECT
public:
explicit SpecialDoubleSpinBox(QWidget* p = nullptr, int h = 16, double step = 0.05);
virtual ~SpecialDoubleSpinBox() { }
protected:
virtual void enterEvent(QEvent* e) override;
virtual void leaveEvent(QEvent* e) override;
virtual bool eventFilter(QObject* o, QEvent* e) override;
};
/// <summary>
/// VariationTreeWidgetItem and VariationTreeDoubleSpinBox need each other, but each can't include the other.
/// So VariationTreeWidgetItem includes this file, and use a forward declaration here.
/// </summary>
class VariationTreeWidgetItem;
/// <summary>
/// Derivation for the double spin boxes that are in the
/// variations tree.
/// </summary>
class VariationTreeDoubleSpinBox : public SpecialDoubleSpinBox
{
Q_OBJECT
public:
explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05);
virtual ~VariationTreeDoubleSpinBox() { }
bool IsParam() { return !m_Param.empty(); }
string ParamName() { return m_Param; }
eVariationId GetVariationId() { return m_Id; }
VariationTreeWidgetItem* WidgetItem() { return m_WidgetItem; }
virtual QString textFromValue(double value) const override;
virtual double valueFromText(const QString& text) const override;
public slots:
void PiActionTriggered(bool checked = false);
void TwoPiActionTriggered(bool checked = false);
void PiOver2ActionTriggered(bool checked = false);
void PiOver3ActionTriggered(bool checked = false);
void PiOver4ActionTriggered(bool checked = false);
void PiOver6ActionTriggered(bool checked = false);
void OneOverPiActionTriggered(bool checked = false);
void TwoOverPiActionTriggered(bool checked = false);
void ThreeOverPiActionTriggered(bool checked = false);
void FourOverPiActionTriggered(bool checked = false);
void SqrtTwoActionTriggered(bool checked = false);
void SqrtThreeActionTriggered(bool checked = false);
private:
string m_Param;
eVariationId m_Id;
VariationTreeWidgetItem* m_WidgetItem;
};
/// <summary>
/// Derivation for the double spin boxes that are in the
/// affine controls.
/// </summary>
class AffineDoubleSpinBox : public SpecialDoubleSpinBox
{
Q_OBJECT
public:
explicit AffineDoubleSpinBox(QWidget* p, int h = 20, double step = 0.01);
virtual ~AffineDoubleSpinBox() { }
public slots:
void NegOneActionTriggered(bool checked = false);
void ZeroActionTriggered(bool checked = false);
void OneActionTriggered(bool checked = false);
void FortyFiveActionTriggered(bool checked = false);
void NegFortyFiveActionTriggered(bool checked = false);
};
#pragma once
#include "FractoriumPch.h"
#include "FractoriumSettings.h"
/// <summary>
/// DoubleSpinBox and VariationTreeDoubleSpinBox classes.
/// </summary>
enum class eSpinToggle : et { NONE = 0, SPIN_DOUBLE_CLICK = 1, SPIN_RIGHT_CLICK = 2 };
/// <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 = nullptr, int height = 16, double step = 0.05, bool clearsel = true);
virtual ~DoubleSpinBox() { }
void SetValueStealth(double d);
void DoubleClick(bool b);
void DoubleClickLowVal(double val);
void DoubleClickZero(double val);
void DoubleClickNonZero(double val);
double Step();
void Step(double step);
double SmallStep();
void SmallStep(double step);
QLineEdit* lineEdit();
public slots:
void OnSpinBoxValueChanged(double d);
void OnTimeout();
protected:
virtual bool eventFilter(QObject* o, QEvent* e) override;
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void focusInEvent(QFocusEvent* e) override;
virtual void focusOutEvent(QFocusEvent* e) override;
virtual void enterEvent(QEnterEvent* e) override;
virtual void leaveEvent(QEvent* e) override;
bool m_DoubleClick;
bool m_ClearSel;
shared_ptr<FractoriumSettings> m_Settings;
private:
void StartTimer();
void StopTimer();
double m_DoubleClickLowVal;
double m_DoubleClickNonZero;
double m_DoubleClickZero;
double m_Step;
double m_SmallStep;
QPoint m_MouseDownPoint;
QPoint m_MouseMovePoint;
static QTimer s_Timer;
};
/// <summary>
/// Thin derivation to implement the eventFilter() override which subsequently derived
/// classes will use to suppress showing the context menu when right clicking is used for toggling,
/// unless shift is pressed.
/// </summary>
class SpecialDoubleSpinBox : public DoubleSpinBox
{
Q_OBJECT
public:
explicit SpecialDoubleSpinBox(QWidget* p = nullptr, int h = 16, double step = 0.05);
virtual ~SpecialDoubleSpinBox() { }
protected:
virtual void enterEvent(QEnterEvent* e) override;
virtual void leaveEvent(QEvent* e) override;
virtual bool eventFilter(QObject* o, QEvent* e) override;
};
/// <summary>
/// VariationTreeWidgetItem and VariationTreeDoubleSpinBox need each other, but each can't include the other.
/// So VariationTreeWidgetItem includes this file, and use a forward declaration here.
/// </summary>
class VariationTreeWidgetItem;
/// <summary>
/// Derivation for the double spin boxes that are in the
/// variations tree.
/// </summary>
class VariationTreeDoubleSpinBox : public SpecialDoubleSpinBox
{
Q_OBJECT
public:
explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05);
virtual ~VariationTreeDoubleSpinBox() { }
bool IsParam() { return !m_Param.empty(); }
string ParamName() { return m_Param; }
eVariationId GetVariationId() { return m_Id; }
VariationTreeWidgetItem* WidgetItem() { return m_WidgetItem; }
virtual QString textFromValue(double value) const override;
virtual double valueFromText(const QString& text) const override;
public slots:
void PiActionTriggered(bool checked = false);
void TwoPiActionTriggered(bool checked = false);
void PiOver2ActionTriggered(bool checked = false);
void PiOver3ActionTriggered(bool checked = false);
void PiOver4ActionTriggered(bool checked = false);
void PiOver6ActionTriggered(bool checked = false);
void OneOverPiActionTriggered(bool checked = false);
void TwoOverPiActionTriggered(bool checked = false);
void ThreeOverPiActionTriggered(bool checked = false);
void FourOverPiActionTriggered(bool checked = false);
void SqrtTwoActionTriggered(bool checked = false);
void SqrtThreeActionTriggered(bool checked = false);
private:
string m_Param;
eVariationId m_Id;
VariationTreeWidgetItem* m_WidgetItem;
};
/// <summary>
/// Derivation for the double spin boxes that are in the
/// affine controls.
/// </summary>
class AffineDoubleSpinBox : public SpecialDoubleSpinBox
{
Q_OBJECT
public:
explicit AffineDoubleSpinBox(QWidget* p, int h = 20, double step = 0.01);
virtual ~AffineDoubleSpinBox() { }
public slots:
void NegOneActionTriggered(bool checked = false);
void ZeroActionTriggered(bool checked = false);
void OneActionTriggered(bool checked = false);
void FortyFiveActionTriggered(bool checked = false);
void NegFortyFiveActionTriggered(bool checked = false);
};

View File

@ -1,89 +1,89 @@
#pragma once
#include "FractoriumPch.h"
#include "DoubleSpinBox.h"
/// <summary>
/// DoubleSpinBoxTableItemDelegate class.
/// </summary>
/// <summary>
/// Used for showing a DoubleSpinBox on the cell of a QTableView when the user enters the cell to edit it.
/// </summary>
class DoubleSpinBoxTableItemDelegate
: public QItemDelegate
{
Q_OBJECT
public:
/// <summary>
/// Constructor that assigns a DoubleSpinBox.
/// </summary>
/// <param name="title">The DoubleSpinBox to use throughought the life of the object</param>
/// <param name="parent">The parent widget. Default: nullptr.</param>
explicit DoubleSpinBoxTableItemDelegate(DoubleSpinBox* spinBox, QObject* parent = nullptr)
: QItemDelegate(parent),
m_SpinBox(spinBox)
{
}
/// <summary>
/// Re-parent and return the DoubleSpinBox to display when the user clicks on a cell and it enters edit mode.
/// The re-parenting is done so that the DoubleSpinBox appears directly on top of the cell.
/// </summary>
/// <param name="parent">The parent cell</param>
/// <param name="option">Ignored</param>
/// <param name="index">Ignored</param>
/// <returns>The DoubleSpinBox member</returns>
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
m_SpinBox->setParent(parent);
return m_SpinBox;
}
/// <summary>
/// Prevent DoubleSpinBox control from being destroyed when the cell loses focus.
/// </summary>
/// <param name="editor">Ignored</param>
/// <param name="index">Ignored</param>
void destroyEditor(QWidget* editor, const QModelIndex& index) const override
{
}
/// <summary>
/// Set the value of the DoubleSpinBox as well as its tableindex property.
/// </summary>
/// <param name="editor">Ignored</param>
/// <param name="index">Ignored</param>
void setEditorData(QWidget* editor, const QModelIndex& index) const override
{
const QPoint p(index.row(), index.column());
const auto value = index.model()->data(index, Qt::EditRole).toDouble();
m_SpinBox->setProperty("tableindex", p);
m_SpinBox->setValue(value);
}
/// <summary>
/// Set the cell in the model to the value of the DoubleSpinBox.
/// </summary>
/// <param name="editor">Ignored</param>
/// <param name="model">The model whose value will be set</param>
/// <param name="index">The cell index of the model</param>
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override
{
model->setData(index, m_SpinBox->value(), Qt::EditRole);
}
/// <summary>
/// Set the geometry of the DoubleSpinBox to match the cell being edited.
/// </summary>
/// <param name="editor">The DoubleSpinBox member</param>
/// <param name="option">Contains the rectangle to be used for the geometry of the DoubleSpinBox</param>
/// <param name="index">Ignored</param>
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
editor->setGeometry(option.rect);
}
private:
DoubleSpinBox* m_SpinBox;
#pragma once
#include "FractoriumPch.h"
#include "DoubleSpinBox.h"
/// <summary>
/// DoubleSpinBoxTableItemDelegate class.
/// </summary>
/// <summary>
/// Used for showing a DoubleSpinBox on the cell of a QTableView when the user enters the cell to edit it.
/// </summary>
class DoubleSpinBoxTableItemDelegate
: public QItemDelegate
{
Q_OBJECT
public:
/// <summary>
/// Constructor that assigns a DoubleSpinBox.
/// </summary>
/// <param name="title">The DoubleSpinBox to use throughought the life of the object</param>
/// <param name="parent">The parent widget. Default: nullptr.</param>
explicit DoubleSpinBoxTableItemDelegate(DoubleSpinBox* spinBox, QObject* parent = nullptr)
: QItemDelegate(parent),
m_SpinBox(spinBox)
{
}
/// <summary>
/// Re-parent and return the DoubleSpinBox to display when the user clicks on a cell and it enters edit mode.
/// The re-parenting is done so that the DoubleSpinBox appears directly on top of the cell.
/// </summary>
/// <param name="parent">The parent cell</param>
/// <param name="option">Ignored</param>
/// <param name="index">Ignored</param>
/// <returns>The DoubleSpinBox member</returns>
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
m_SpinBox->setParent(parent);
return m_SpinBox;
}
/// <summary>
/// Prevent DoubleSpinBox control from being destroyed when the cell loses focus.
/// </summary>
/// <param name="editor">Ignored</param>
/// <param name="index">Ignored</param>
void destroyEditor(QWidget* editor, const QModelIndex& index) const override
{
}
/// <summary>
/// Set the value of the DoubleSpinBox as well as its tableindex property.
/// </summary>
/// <param name="editor">Ignored</param>
/// <param name="index">Ignored</param>
void setEditorData(QWidget* editor, const QModelIndex& index) const override
{
const QPoint p(index.row(), index.column());
const auto value = index.model()->data(index, Qt::EditRole).toDouble();
m_SpinBox->setProperty("tableindex", p);
m_SpinBox->setValue(value);
}
/// <summary>
/// Set the cell in the model to the value of the DoubleSpinBox.
/// </summary>
/// <param name="editor">Ignored</param>
/// <param name="model">The model whose value will be set</param>
/// <param name="index">The cell index of the model</param>
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override
{
model->setData(index, m_SpinBox->value(), Qt::EditRole);
}
/// <summary>
/// Set the geometry of the DoubleSpinBox to match the cell being edited.
/// </summary>
/// <param name="editor">The DoubleSpinBox member</param>
/// <param name="option">Contains the rectangle to be used for the geometry of the DoubleSpinBox</param>
/// <param name="index">Ignored</param>
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
editor->setGeometry(option.rect);
}
private:
DoubleSpinBox* m_SpinBox;
};

View File

@ -1,235 +1,235 @@
#pragma once
#include "FractoriumCommon.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>
/// Default constructor and destructor.
/// </summary>
EmberFile() = default;
~EmberFile() = default;
/// <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;
CopyCont(m_Embers, emberFile.m_Embers);
return *this;
}
/// <summary>
/// Move constructor.
/// </summary>
/// <param name="emberFile">The EmberFile object to move</param>
EmberFile(EmberFile<T>&& emberFile)
{
EmberFile<T>::operator=<T>(emberFile);
}
/// <summary>
/// Move assignment operator.
/// </summary>
/// <param name="emberFile">The EmberFile object to move</param>
EmberFile<T>& operator = (EmberFile<T>&& emberFile)
{
if (this != &emberFile)
{
m_Filename = emberFile.m_Filename;
m_Embers = std::move(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>
/// Thin wrapper to get the size of the vector of embers.
/// </summary>
size_t Size()
{
return m_Embers.size();
}
/// <summary>
/// Get a pointer to the ember at the specified index.
/// </summary>
/// <param name="i">The index of the ember to retrieve</param>
/// <returns>A pointer to the ember if it was within bounds, else nullptr.</returns>
Ember<T>* Get(size_t i)
{
if (i < m_Embers.size())
return &(*Advance(m_Embers.begin(), i));
return nullptr;
}
/// <summary>
/// Delete the ember at the given index.
/// Will not delete anything if the size is already 1.
/// </summary>
/// <param name="index">The index of the ember to delete</param>
/// <returns>True if successfully deleted, else false.</returns>
bool Delete(size_t index)
{
if (Size() > 1 && index < Size())
{
m_Embers.erase(Advance(m_Embers.begin(), index));
return true;
}
else
return false;
}
/// <summary>
/// Ensure all ember names are unique.
/// </summary>
void MakeNamesUnique()
{
for (auto it1 = m_Embers.begin(); it1 != m_Embers.end(); ++it1)
{
for (auto it2 = m_Embers.begin(); it2 != m_Embers.end(); ++it2)
{
if (it1 != it2 && it1->m_Name == it2->m_Name)
{
it2->m_Name = IncrementTrailingUnderscoreInt(QString::fromStdString(it2->m_Name)).toStdString();
it2 = m_Embers.begin();
}
}
}
}
/// <summary>
/// Return the default filename based on the current date/time.
/// </summary>
/// <returns>The default filename</returns>
static QString DefaultFilename(QString prefix = "Flame_")
{
return prefix + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss");
}
/// <summary>
/// Return a copy of the string which ends with _# where # is the
/// previous number at that position incremented by one.
/// If the original string did not end with _#, the returned
/// string will just have _1 appended to it.
/// </summary>
/// <param name="str">The string to process</param>
/// <returns>The original string with the number after the final _ character incremented by one</returns>
static QString IncrementTrailingUnderscoreInt(const QString& str)
{
bool ok = false;
size_t num = 0;
QString endSection;
QString ret = str;
const auto lastUnderscore = str.lastIndexOf('_');
if (lastUnderscore != -1)
{
endSection = str.section('_', -1);
num = endSection.toULongLong(&ok);
if (ok)
ret.chop(str.size() - lastUnderscore);
}
ret += "_" + QString::number(num + 1);
return ret;
}
/// <summary>
/// Ensures a given input filename is unique by appending a count to the end.
/// </summary>
/// <param name="filename">The filename to ensure is unique</param>
/// <returns>The passed in name if it was unique, else a uniquely made name.</returns>
static QString UniqueFilename(const QString& filename)
{
if (!QFile::exists(filename))
return filename;
QString newPath;
QFileInfo original(filename);
QString base = original.completeBaseName();
const QString path = original.absolutePath() + '/';
const QString extension = original.suffix();
newPath = path + base + "." + extension;
while (QFile::exists(newPath))
{
base = IncrementTrailingUnderscoreInt(base);
newPath = path + base + "." + extension;
}
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(T i)
{
return DefaultFilename() + "_" + ToString<T>(i);
}
QString m_Filename;
list<Ember<T>> m_Embers;
};
#pragma once
#include "FractoriumCommon.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>
/// Default constructor and destructor.
/// </summary>
EmberFile() = default;
~EmberFile() = default;
/// <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;
CopyCont(m_Embers, emberFile.m_Embers);
return *this;
}
/// <summary>
/// Move constructor.
/// </summary>
/// <param name="emberFile">The EmberFile object to move</param>
EmberFile(EmberFile<T>&& emberFile)
{
EmberFile<T>::operator=<T>(emberFile);
}
/// <summary>
/// Move assignment operator.
/// </summary>
/// <param name="emberFile">The EmberFile object to move</param>
EmberFile<T>& operator = (EmberFile<T>&& emberFile)
{
if (this != &emberFile)
{
m_Filename = emberFile.m_Filename;
m_Embers = std::move(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>
/// Thin wrapper to get the size of the vector of embers.
/// </summary>
size_t Size()
{
return m_Embers.size();
}
/// <summary>
/// Get a pointer to the ember at the specified index.
/// </summary>
/// <param name="i">The index of the ember to retrieve</param>
/// <returns>A pointer to the ember if it was within bounds, else nullptr.</returns>
Ember<T>* Get(size_t i)
{
if (i < m_Embers.size())
return &(*Advance(m_Embers.begin(), i));
return nullptr;
}
/// <summary>
/// Delete the ember at the given index.
/// Will not delete anything if the size is already 1.
/// </summary>
/// <param name="index">The index of the ember to delete</param>
/// <returns>True if successfully deleted, else false.</returns>
bool Delete(size_t index)
{
if (Size() > 1 && index < Size())
{
m_Embers.erase(Advance(m_Embers.begin(), index));
return true;
}
else
return false;
}
/// <summary>
/// Ensure all ember names are unique.
/// </summary>
void MakeNamesUnique()
{
for (auto it1 = m_Embers.begin(); it1 != m_Embers.end(); ++it1)
{
for (auto it2 = m_Embers.begin(); it2 != m_Embers.end(); ++it2)
{
if (it1 != it2 && it1->m_Name == it2->m_Name)
{
it2->m_Name = IncrementTrailingUnderscoreInt(QString::fromStdString(it2->m_Name)).toStdString();
it2 = m_Embers.begin();
}
}
}
}
/// <summary>
/// Return the default filename based on the current date/time.
/// </summary>
/// <returns>The default filename</returns>
static QString DefaultFilename(QString prefix = "Flame_")
{
return prefix + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss");
}
/// <summary>
/// Return a copy of the string which ends with _# where # is the
/// previous number at that position incremented by one.
/// If the original string did not end with _#, the returned
/// string will just have _1 appended to it.
/// </summary>
/// <param name="str">The string to process</param>
/// <returns>The original string with the number after the final _ character incremented by one</returns>
static QString IncrementTrailingUnderscoreInt(const QString& str)
{
bool ok = false;
size_t num = 0;
QString endSection;
QString ret = str;
const auto lastUnderscore = str.lastIndexOf('_');
if (lastUnderscore != -1)
{
endSection = str.section('_', -1);
num = endSection.toULongLong(&ok);
if (ok)
ret.chop(str.size() - lastUnderscore);
}
ret += "_" + QString::number(num + 1);
return ret;
}
/// <summary>
/// Ensures a given input filename is unique by appending a count to the end.
/// </summary>
/// <param name="filename">The filename to ensure is unique</param>
/// <returns>The passed in name if it was unique, else a uniquely made name.</returns>
static QString UniqueFilename(const QString& filename)
{
if (!QFile::exists(filename))
return filename;
QString newPath;
QFileInfo original(filename);
QString base = original.completeBaseName();
const QString path = original.absolutePath() + '/';
const QString extension = original.suffix();
newPath = path + base + "." + extension;
while (QFile::exists(newPath))
{
base = IncrementTrailingUnderscoreInt(base);
newPath = path + base + "." + extension;
}
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(T i)
{
return DefaultFilename() + "_" + ToString<T>(i);
}
QString m_Filename;
list<Ember<T>> m_Embers;
};

View File

@ -1,142 +1,142 @@
#pragma once
#include "FractoriumPch.h"
template <typename T> class FractoriumEmberController;
/// <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:
friend FractoriumEmberController<float>;
#ifdef DO_DOUBLE
friend FractoriumEmberController<double>;
#endif
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItemBase(QTreeWidget* p)
: QTreeWidgetItem(p)
{
}
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItemBase(QTreeWidgetItem* p)
: QTreeWidgetItem(p)
{
}
~EmberTreeWidgetItemBase()
{
//qDebug() << "~EmberTreeWidgetItemBase()";
}
/// <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<byte>& v, uint width, uint height)
{
constexpr auto size = PREVIEW_SIZE;
m_Image = QImage(width, height, QImage::Format_RGBA8888);
memcpy(m_Image.scanLine(0), v.data(), SizeOf(v));//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);
}
void SetRendered()
{
m_Rendered = true;
}
protected:
QImage m_Image;
QPixmap m_Pixmap;
bool m_Rendered;
};
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItem(Ember<T>* ember, QTreeWidget* p = nullptr)
: EmberTreeWidgetItemBase(p),
m_Ember(ember)
{
setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
setCheckState(0, Qt::Unchecked);
}
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItem(Ember<T>* ember, QTreeWidgetItem* p = nullptr)
: EmberTreeWidgetItemBase(p),
m_Ember(ember)
{
setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
setCheckState(0, Qt::Unchecked);
}
/// <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;
};
#pragma once
#include "FractoriumPch.h"
template <typename T> class FractoriumEmberController;
/// <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:
friend FractoriumEmberController<float>;
#ifdef DO_DOUBLE
friend FractoriumEmberController<double>;
#endif
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItemBase(QTreeWidget* p)
: QTreeWidgetItem(p)
{
}
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItemBase(QTreeWidgetItem* p)
: QTreeWidgetItem(p)
{
}
~EmberTreeWidgetItemBase()
{
//qDebug() << "~EmberTreeWidgetItemBase()";
}
/// <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, uint width, uint height)
{
constexpr auto size = PREVIEW_SIZE;
m_Image = QImage(width, height, QImage::Format_RGBA8888);
memcpy(m_Image.scanLine(0), v.data(), SizeOf(v));//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);
}
void SetRendered()
{
m_Rendered = true;
}
protected:
QImage m_Image;
QPixmap m_Pixmap;
bool m_Rendered = false;
};
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItem(Ember<T>* ember, QTreeWidget* p = nullptr)
: EmberTreeWidgetItemBase(p),
m_Ember(ember)
{
setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
setCheckState(0, Qt::Unchecked);
}
/// <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="p">The parent widget of this item</param>
explicit EmberTreeWidgetItem(Ember<T>* ember, QTreeWidgetItem* p = nullptr)
: EmberTreeWidgetItemBase(p),
m_Ember(ember)
{
setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
setCheckState(0, Qt::Unchecked);
}
/// <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;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,156 +1,157 @@
#pragma once
#include "ui_FinalRenderDialog.h"
#include "SpinBox.h"
#include "DoubleSpinBox.h"
#include "TwoButtonComboWidget.h"
#include "FractoriumSettings.h"
#include "FinalRenderEmberController.h"
/// <summary>
/// FractoriumFinalRenderDialog class.
/// </summary>
class Fractorium;//Forward declaration since Fractorium uses this dialog.
class FinalRenderEmberControllerBase;
template <typename T> class FinalRenderEmberController;
/// <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>;
friend PreviewRenderer<float>;
friend FinalRenderPreviewRenderer<float>;
#ifdef DO_DOUBLE
friend FinalRenderEmberController<double>;
friend PreviewRenderer<double>;
friend FinalRenderPreviewRenderer<double>;
#endif
public:
FractoriumFinalRenderDialog(QWidget* p, Qt::WindowFlags f = 0);
~FractoriumFinalRenderDialog();
void Show(bool fromSequence);
bool EarlyClip();
bool YAxisUp();
bool Transparency();
bool OpenCL();
bool Double();
bool SaveXml();
bool DoAll();
bool DoSequence();
bool Png16Bit();
bool KeepAspect();
bool ApplyToAll();
eScaleType Scale();
void Scale(eScaleType scale);
QString Ext();
QString Path();
void Path(const QString& s);
QString Prefix();
QString Suffix();
uint Current();
uint ThreadCount();
int ThreadPriority();
double OpenCLSubBatchPct();
double WidthScale();
double HeightScale();
double Quality();
uint TemporalSamples();
uint Supersample();
uint Strips();
QList<QVariant> Devices();
FinalRenderGuiState State();
public slots:
void MoveCursorToEnd();
void OnEarlyClipCheckBoxStateChanged(int state);
void OnYAxisUpCheckBoxStateChanged(int state);
void OnTransparencyCheckBoxStateChanged(int state);
void OnOpenCLCheckBoxStateChanged(int state);
void OnDoublePrecisionCheckBoxStateChanged(int state);
void OnDoAllCheckBoxStateChanged(int state);
void OnDoSequenceCheckBoxStateChanged(int state);
void OnCurrentSpinChanged(int d);
void OnApplyAllCheckBoxStateChanged(int state);
void OnWidthScaleChanged(double d);
void OnWidthChanged(int d);
void OnHeightScaleChanged(double d);
void OnHeightChanged(int d);
void OnKeepAspectCheckBoxStateChanged(int state);
void OnScaleRadioButtonChanged(bool checked);
void OnDeviceTableCellChanged(int row, int col);
void OnDeviceTableRadioToggled(bool checked);
void OnQualityChanged(double d);
void OnTemporalSamplesChanged(int d);
void OnSupersampleChanged(int d);
void OnStripsChanged(int d);
void OnFileButtonClicked(bool checked);
void OnShowFolderButtonClicked(bool checked);
void OnExtIndexChanged(int d);
void OnPrefixChanged(const QString& s);
void OnSuffixChanged(const QString& s);
void OnQualityBumpClicked();
void OnSaveAgainAsClicked();
void OnRenderClicked(bool checked);
void OnPauseClicked(bool checked);
void OnCancelRenderClicked(bool checked);
void Pause(bool paused);
virtual void reject() override;
protected:
virtual void showEvent(QShowEvent* e) override;
private:
bool CreateControllerFromGUI(bool createRenderer);
bool SetMemory();
static QString m_Prefix;
static QString m_Suffix;
bool m_FromSequence;
int m_MemoryCellIndex;
int m_ItersCellIndex;
int m_PathCellIndex;
Timing m_RenderTimer;
DoubleSpinBox* m_WidthScaleSpin;
SpinBox* m_WidthSpin;
DoubleSpinBox* m_HeightScaleSpin;
SpinBox* m_HeightSpin;
DoubleIntSpinnerWidget* m_WidthSpinnerWidget;
DoubleIntSpinnerWidget* m_HeightSpinnerWidget;
DoubleSpinBox* m_QualitySpin;
SpinBox* m_TemporalSamplesSpin;
SpinBox* m_SupersampleSpin;
SpinBox* m_StripsSpin;
TwoButtonComboWidget* m_Tbcw;
QLineEdit* m_PrefixEdit;
QLineEdit* m_SuffixEdit;
shared_ptr<FractoriumSettings> m_Settings;
Fractorium* m_Fractorium;
shared_ptr<OpenCLInfo> m_Info;
vector<OpenCLWrapper> m_Wrappers;
unique_ptr<FinalRenderEmberControllerBase> m_Controller;
Ui::FinalRenderDialog ui;
};
#pragma once
#include "ui_FinalRenderDialog.h"
#include "SpinBox.h"
#include "DoubleSpinBox.h"
#include "TwoButtonComboWidget.h"
#include "FractoriumSettings.h"
#include "FinalRenderEmberController.h"
/// <summary>
/// FractoriumFinalRenderDialog class.
/// </summary>
class Fractorium;//Forward declaration since Fractorium uses this dialog.
class FinalRenderEmberControllerBase;
template <typename T> class FinalRenderEmberController;
/// <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>;
friend PreviewRenderer<float>;
friend FinalRenderPreviewRenderer<float>;
#ifdef DO_DOUBLE
friend FinalRenderEmberController<double>;
friend PreviewRenderer<double>;
friend FinalRenderPreviewRenderer<double>;
#endif
public:
FractoriumFinalRenderDialog(QWidget* p, Qt::WindowFlags f = Qt::WindowType::Widget);
~FractoriumFinalRenderDialog();
void Show(bool fromSequence);
bool EarlyClip();
bool YAxisUp();
bool Transparency();
bool OpenCL();
bool Double();
bool SaveXml();
bool DoAll();
bool DoSequence();
bool Png16Bit();
bool KeepAspect();
bool ApplyToAll();
eScaleType Scale();
void Scale(eScaleType scale);
QString Ext();
QString Path();
void Path(const QString& s);
QString Prefix();
QString Suffix();
uint Current();
uint ThreadCount();
int ThreadPriority();
double OpenCLSubBatchPct();
double WidthScale();
double HeightScale();
double Quality();
uint TemporalSamples();
uint Supersample();
uint Strips();
QList<QVariant> Devices();
FinalRenderGuiState State();
public slots:
void MoveCursorToEnd();
void OnEarlyClipCheckBoxStateChanged(int state);
void OnYAxisUpCheckBoxStateChanged(int state);
void OnTransparencyCheckBoxStateChanged(int state);
void OnOpenCLCheckBoxStateChanged(int state);
void OnDoublePrecisionCheckBoxStateChanged(int state);
void OnDoAllCheckBoxStateChanged(int state);
void OnDoSequenceCheckBoxStateChanged(int state);
void OnCurrentSpinChanged(int d);
void OnApplyAllCheckBoxStateChanged(int state);
void OnWidthScaleChanged(double d);
void OnWidthChanged(int d);
void OnHeightScaleChanged(double d);
void OnHeightChanged(int d);
void OnKeepAspectCheckBoxStateChanged(int state);
void OnScaleRadioButtonChanged(bool checked);
void OnDeviceTableCellChanged(int row, int col);
void OnDeviceTableRadioToggled(bool checked);
void OnQualityChanged(double d);
void OnTemporalSamplesChanged(int d);
void OnSupersampleChanged(int d);
void OnStripsChanged(int d);
void OnFileButtonClicked(bool checked);
void OnShowFolderButtonClicked(bool checked);
void OnExtIndexChanged(int d);
void OnPrefixChanged(const QString& s);
void OnSuffixChanged(const QString& s);
void OnQualityBumpClicked();
void OnSaveAgainAsClicked();
void OnRenderClicked(bool checked);
void OnPauseClicked(bool checked);
void OnCancelRenderClicked(bool checked);
void Pause(bool paused);
virtual void reject() override;
protected:
virtual void showEvent(QShowEvent* e) override;
private:
bool CreateControllerFromGUI(bool createRenderer);
bool SetMemory();
static QString m_Prefix;
static QString m_Suffix;
bool m_FromSequence;
int m_MemoryCellIndex;
int m_ItersCellIndex;
int m_PathCellIndex;
Timing m_RenderTimer;
DoubleSpinBox* m_WidthScaleSpin;
SpinBox* m_WidthSpin;
DoubleSpinBox* m_HeightScaleSpin;
DoubleSpinBox* m_SubBatchPctSpin;
SpinBox* m_HeightSpin;
DoubleIntSpinnerWidget* m_WidthSpinnerWidget;
DoubleIntSpinnerWidget* m_HeightSpinnerWidget;
DoubleSpinBox* m_QualitySpin;
SpinBox* m_TemporalSamplesSpin;
SpinBox* m_SupersampleSpin;
SpinBox* m_StripsSpin;
TwoButtonComboWidget* m_Tbcw;
QLineEdit* m_PrefixEdit;
QLineEdit* m_SuffixEdit;
shared_ptr<FractoriumSettings> m_Settings;
Fractorium* m_Fractorium;
shared_ptr<OpenCLInfo> m_Info;
vector<OpenCLWrapper> m_Wrappers;
unique_ptr<FinalRenderEmberControllerBase> m_Controller;
Ui::FinalRenderDialog ui;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,187 +1,187 @@
#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;
template <typename T> class FinalRenderPreviewRenderer;
/// <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_YAxisUp;
bool m_AlphaChannel;
bool m_Transparency;
bool m_OpenCL;
bool m_Double;
bool m_SaveXml;
bool m_DoAll;
bool m_Png16Bit;
bool m_DoSequence;
bool m_KeepAspect;
eScaleType m_Scale;
QString m_Path;
QString m_Ext;
QString m_Prefix;
QString m_Suffix;
QList<QVariant> m_Devices;
uint m_ThreadCount;
int m_ThreadPriority;
double m_SubBatchPct;
double m_WidthScale;
double m_HeightScale;
double m_Quality;
uint m_TemporalSamples;
uint m_Supersample;
size_t m_Strips;
};
/// <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* finalRenderDialog);
virtual ~FinalRenderEmberControllerBase() { }
virtual void SyncCurrentToGui() { }
virtual void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true) { }
virtual void SyncCurrentToSizeSpinners(bool scale, bool size, bool doWidth = true, bool doHeight = true) { }
virtual void ResetProgress(bool total = true) { }
virtual tuple<size_t, size_t, size_t> SyncAndComputeMemory() { return tuple<size_t, size_t, size_t>(0, 0, 0); }
virtual double OriginalAspect() { return 1; }
virtual QString ComposePath(const QString& name, bool unique = true) { return ""; }
virtual bool BumpQualityRender(double d) { return false; }
virtual QString SaveCurrentAgain() { return ""; }
virtual void CancelRender() { }
virtual QString CheckMemory(const tuple<size_t, size_t, size_t>& p) { return ""; }
bool Running() { return m_Result.isRunning(); }
bool CreateRendererFromGUI();
void Output(const QString& s);
protected:
bool m_Run = false;
bool m_IsQualityBump = false;
size_t m_ImageCount = 0;
std::atomic<size_t> m_FinishedImageCount;
QFuture<void> m_Result;
std::function<void (void)> m_FinalRenderFunc;
shared_ptr<FractoriumSettings> m_Settings;
FractoriumFinalRenderDialog* m_FinalRenderDialog;
FinalRenderGuiState m_GuiState;
std::recursive_mutex m_ProgressCs;
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
{
friend FinalRenderPreviewRenderer<T>;
public:
FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender);
virtual ~FinalRenderEmberController() { }
//Virtual functions overridden from FractoriumEmberControllerBase.
void SetEmberFile(const EmberFile<float>& emberFile, bool move) override;
void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
#ifdef DO_DOUBLE
void SetEmberFile(const EmberFile<double>& emberFile, bool move) override;
void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
#endif
void SetEmber(size_t index, bool verbatim) override;
void SaveCurrentAsXml(QString filename = "") override;
bool Render() override;
bool BumpQualityRender(double d) override;
bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared = true) override;
int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
size_t Index() const override { return m_Ember->m_Index; }
uint SizeOfT() const override { return sizeof(T); }
//Virtual functions overridden from FinalRenderEmberControllerBase.
void SyncCurrentToGui() override;
void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true) override;
void SyncCurrentToSizeSpinners(bool scale, bool size, bool doWidth = true, bool doHeight = true) override;
void ResetProgress(bool total = true) override;
tuple<size_t, size_t, size_t> SyncAndComputeMemory() override;
double OriginalAspect() override { return double(m_Ember->m_OrigFinalRasW) / m_Ember->m_OrigFinalRasH; }
QString Name() const override { return QString::fromStdString(m_Ember->m_Name); }
QString ComposePath(const QString& name, bool unique = true) override;
QString SaveCurrentAgain() override;
void CancelRender() override;
QString CheckMemory(const tuple<size_t, size_t, size_t>& p) override;
//Non Virtual functions.
EmberNs::Renderer<T, float>* FirstOrDefaultRenderer();
protected:
void Pause(bool pause) override;
bool Paused() override;
void HandleFinishedProgress();
QString SaveCurrentRender(Ember<T>& ember);
QString SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency);
void RenderComplete(Ember<T>& ember);
void RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer);
void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true);
bool SyncGuiToRenderer();
void SetProgressComplete(int val);
bool RenderSingleEmber(Ember<T>& ember, bool fullRender, size_t &stripForProgress);
bool RenderSingleEmberFromSeries(std::atomic<size_t>* atomfTime, size_t index);
Ember<T>* m_Ember;
EmberFile<T> m_EmberFile;
EmberToXml<T> m_XmlWriter;
unique_ptr<FinalRenderPreviewRenderer<T>> m_FinalPreviewRenderer;
vector<unique_ptr<EmberNs::Renderer<T, float>>> m_Renderers;
};
/// <summary>
/// Thin derivation to handle preview rendering that is specific to the final render dialog.
/// This differs from the preview renderers on the main window because they render multiple embers
/// to a tree, whereas this renders a single preview.
/// </summary>
template <typename T>
class FinalRenderPreviewRenderer : public PreviewRenderer<T>
{
public:
using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewVec;
using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage;
FinalRenderPreviewRenderer(FinalRenderEmberController<T>* controller) : m_Controller(controller)
{
}
void PreviewRenderFunc(uint start, uint end) override;
private:
FinalRenderEmberController<T>* m_Controller;
};
#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;
template <typename T> class FinalRenderPreviewRenderer;
/// <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_YAxisUp;
bool m_AlphaChannel;
bool m_Transparency;
bool m_OpenCL;
bool m_Double;
bool m_SaveXml;
bool m_DoAll;
bool m_Png16Bit;
bool m_DoSequence;
bool m_KeepAspect;
eScaleType m_Scale;
QString m_Path;
QString m_Ext;
QString m_Prefix;
QString m_Suffix;
QList<QVariant> m_Devices;
uint m_ThreadCount;
int m_ThreadPriority;
double m_SubBatchPct;
double m_WidthScale;
double m_HeightScale;
double m_Quality;
uint m_TemporalSamples;
uint m_Supersample;
size_t m_Strips;
};
/// <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* finalRenderDialog);
virtual ~FinalRenderEmberControllerBase() { }
virtual void SyncCurrentToGui() { }
virtual void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true) { }
virtual void SyncCurrentToSizeSpinners(bool scale, bool size, bool doWidth = true, bool doHeight = true) { }
virtual void ResetProgress(bool total = true) { }
virtual tuple<size_t, size_t, size_t> SyncAndComputeMemory() { return tuple<size_t, size_t, size_t>(0, 0, 0); }
virtual double OriginalAspect() { return 1; }
virtual QString ComposePath(const QString& name, bool unique = true) { return ""; }
virtual bool BumpQualityRender(double d) { return false; }
virtual QString SaveCurrentAgain() { return ""; }
virtual void CancelRender() { }
virtual QString CheckMemory(const tuple<size_t, size_t, size_t>& p) { return ""; }
bool Running() { return m_Result.isRunning(); }
bool CreateRendererFromGUI();
void Output(const QString& s);
protected:
bool m_Run = false;
bool m_IsQualityBump = false;
size_t m_ImageCount = 0;
std::atomic<size_t> m_FinishedImageCount;
QFuture<void> m_Result;
std::function<void (void)> m_FinalRenderFunc;
shared_ptr<FractoriumSettings> m_Settings;
FractoriumFinalRenderDialog* m_FinalRenderDialog;
FinalRenderGuiState m_GuiState;
std::recursive_mutex m_ProgressCs;
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
{
friend FinalRenderPreviewRenderer<T>;
public:
FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender);
virtual ~FinalRenderEmberController() { }
//Virtual functions overridden from FractoriumEmberControllerBase.
void SetEmberFile(const EmberFile<float>& emberFile, bool move) override;
void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
#ifdef DO_DOUBLE
void SetEmberFile(const EmberFile<double>& emberFile, bool move) override;
void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
#endif
void SetEmber(size_t index, bool verbatim) override;
void SaveCurrentAsXml(QString filename = "") override;
bool Render() override;
bool BumpQualityRender(double d) override;
bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared = true) override;
int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
size_t Index() const noexcept override { return m_Ember->m_Index; }
uint SizeOfT() const noexcept override { return sizeof(T); }
//Virtual functions overridden from FinalRenderEmberControllerBase.
void SyncCurrentToGui() override;
void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true) override;
void SyncCurrentToSizeSpinners(bool scale, bool size, bool doWidth = true, bool doHeight = true) override;
void ResetProgress(bool total = true) override;
tuple<size_t, size_t, size_t> SyncAndComputeMemory() override;
double OriginalAspect() override { return double(m_Ember->m_OrigFinalRasW) / m_Ember->m_OrigFinalRasH; }
QString Name() const override { return QString::fromStdString(m_Ember->m_Name); }
QString ComposePath(const QString& name, bool unique = true) override;
QString SaveCurrentAgain() override;
void CancelRender() override;
QString CheckMemory(const tuple<size_t, size_t, size_t>& p) override;
//Non Virtual functions.
EmberNs::Renderer<T, float>* FirstOrDefaultRenderer();
protected:
void Pause(bool pause) override;
bool Paused() override;
void HandleFinishedProgress();
QString SaveCurrentRender(Ember<T>& ember);
QString SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency);
void RenderComplete(Ember<T>& ember);
void RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer);
void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true);
bool SyncGuiToRenderer();
void SetProgressComplete(int val);
bool RenderSingleEmber(Ember<T>& ember, bool fullRender, size_t& stripForProgress);
bool RenderSingleEmberFromSeries(std::atomic<size_t>* atomfTime, size_t index);
Ember<T>* m_Ember;
EmberFile<T> m_EmberFile;
EmberToXml<T> m_XmlWriter;
unique_ptr<FinalRenderPreviewRenderer<T>> m_FinalPreviewRenderer;
vector<unique_ptr<EmberNs::Renderer<T, float>>> m_Renderers;
};
/// <summary>
/// Thin derivation to handle preview rendering that is specific to the final render dialog.
/// This differs from the preview renderers on the main window because they render multiple embers
/// to a tree, whereas this renders a single preview.
/// </summary>
template <typename T>
class FinalRenderPreviewRenderer : public PreviewRenderer<T>
{
public:
using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewVec;
using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage;
FinalRenderPreviewRenderer(FinalRenderEmberController<T>* controller) : m_Controller(controller)
{
}
void PreviewRenderFunc(uint start, uint end) override;
private:
FinalRenderEmberController<T>* m_Controller;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +1,55 @@
<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/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/link-add.png</file>
<file>Icons/eraser.png</file>
<file>Icons/editraise.png</file>
<file>Icons/square.png</file>
<file>Icons/cube.png</file>
<file>Icons/table_gear.png</file>
<file>Icons/checkbox_checked.png</file>
<file>Icons/checkbox_unchecked.png</file>
<file>Icons/control.png</file>
<file>Icons/control-stop-square.png</file>
<file>Icons/Function-512.png</file>
<file>Icons/pic.png</file>
<file>Icons/grid.png</file>
<file>Icons/reset_scale.png</file>
</qresource>
</RCC>
<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/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/link-add.png</file>
<file>Icons/eraser.png</file>
<file>Icons/editraise.png</file>
<file>Icons/square.png</file>
<file>Icons/cube.png</file>
<file>Icons/table_gear.png</file>
<file>Icons/checkbox_checked.png</file>
<file>Icons/checkbox_unchecked.png</file>
<file>Icons/control.png</file>
<file>Icons/control-stop-square.png</file>
<file>Icons/Function-512.png</file>
<file>Icons/pic.png</file>
<file>Icons/grid.png</file>
<file>Icons/reset_scale.png</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,433 +1,433 @@
#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)
: m_PaletteList(PaletteList<float>::Instance())
{
Timing t;
m_Fractorium = fractorium;
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 = make_unique<QTimer>(m_Fractorium);
m_RenderTimer->setInterval(0);
m_Fractorium->connect(m_RenderTimer.get(), SIGNAL(timeout()), SLOT(IdleTimer()));
m_RenderRestartTimer = make_unique<QTimer>(m_Fractorium);
m_AnimateTimer = make_unique<QTimer>(m_Fractorium);
m_AnimateTimer->stop();
m_Fractorium->connect(m_RenderRestartTimer.get(), &QTimer::timeout, [&]() { m_Fractorium->StartRenderTimer(false); });//It's ok to pass false for the first shot because creating the controller will start the preview renders.
// XXX: why not SLOT(SequenceAnimateNextFrame())?
m_Fractorium->connect(m_AnimateTimer.get(), &QTimer::timeout, [&]() { SequenceAnimateNextFrame(); });
}
/// <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);
m_RenderTimer->stop();
m_RenderRestartTimer->stop();
m_AnimateTimer->stop();
}
/// <summary>
/// Pause or resume the renderer.
/// </summary>
/// <param name="pause">True to pause, false to unpause.</param>
void FractoriumEmberControllerBase::Pause(bool pause)
{
m_Renderer->Pause(pause);
}
/// <summary>
/// Retrieve the paused state of the renderer.
/// </summary>
/// <returns>True if the renderer is paused, else false.</returns>
bool FractoriumEmberControllerBase::Paused()
{
return m_Renderer->Paused();
}
/// <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_VariationList(VariationList<T>::Instance())
{
size_t b = 0;
m_GLController = make_unique<GLEmberController<T>>(fractorium, fractorium->ui.GLDisplay, this);
m_LibraryPreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.LibraryTree, m_EmberFile);
m_SequencePreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.SequenceTree, m_SequenceFile);
m_PaletteList->Clear();
m_Fractorium->ui.PaletteFilenameCombo->clear();
//Initial combo change event to fill the palette table will be called automatically later.
//Look hard for a palette.
const auto paths = GetDefaultPaths();
for (auto& path : paths)
b |= InitPaletteList(path);
if (b)
{
m_SheepTools = make_unique<SheepTools<T, float>>(m_PaletteList->Name(0), new EmberNs::Renderer<T, float>());
}
else
{
QString allPaths;
for (auto& path : paths)
allPaths += path + "\r\n";
allPaths = QString("No palettes found in paths:\r\n") + allPaths + "\r\nExiting.";
std::runtime_error ex(allPaths.toStdString());
throw ex;
}
if (m_PaletteList->Size() >= 1)//Only add the user palette if the folder already had a palette, which means we'll be using this folder.
if (m_PaletteList->AddEmptyPaletteFile((GetDefaultUserPath() + "/user-palettes.xml").toStdString()))
m_Fractorium->ui.PaletteFilenameCombo->addItem("user-palettes.xml");
BackgroundChanged(QColor(0, 0, 0));//Default to black.
ClearUndo();
}
/// <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, bool updatePointer) { SetEmberPrivate<float>(ember, verbatim, updatePointer); }
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation)
{
if (sequence)
{
emberFile.m_Filename = m_SequenceFile.m_Filename;
CopyCont(emberFile.m_Embers, m_SequenceFile.m_Embers, perEmberOperation);
}
else
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
}
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, bool updatePointer) { SetEmberPrivate<double>(ember, verbatim, updatePointer); }
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation)
{
if (sequence)
{
emberFile.m_Filename = m_SequenceFile.m_Filename;
CopyCont(emberFile.m_Embers, m_SequenceFile.m_Embers, perEmberOperation);
}
else
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
}
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; }
template <typename T>
void FractoriumEmberController<T>::ConstrainDimensions(Ember<T>& ember)
{
ember.m_FinalRasW = std::min<int>(m_Fractorium->ui.GLDisplay->MaxTexSize(), int(ember.m_FinalRasW));
ember.m_FinalRasH = std::min<int>(m_Fractorium->ui.GLDisplay->MaxTexSize(), int(ember.m_FinalRasH));
}
/// <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>
/// <param name="verbatim">If true, do not overwrite temporal samples, quality or supersample value, else overwrite.</param>
template <typename T>
void FractoriumEmberController<T>::SetEmber(size_t index, bool verbatim)
{
if (index < m_EmberFile.Size())
{
m_Fractorium->SelectLibraryItem(index);
ClearUndo();
SetEmber(*m_EmberFile.Get(index), verbatim, true);
}
}
/// <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: true.</param>
/// <param name="action">The action to add to the rendering queue. Default: eProcessAction::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 ember and optionally all other embers in the file.
/// 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: true.</param>
/// <param name="action">The action to add to the rendering queue. Default: eProcessAction::FULL_RENDER.</param>
/// <param name="applyAll">True to apply the action to all embers in the file in addition to the curent one, false to apply the action only to the current one.</param>
template <typename T>
void FractoriumEmberController<T>::UpdateAll(std::function<void(Ember<T>& ember, bool isMain)> func, bool updateRender, eProcessAction action, bool applyAll)
{
func(m_Ember, true);
if (applyAll)
for (auto& it : m_EmberFile.m_Embers)
func(it, false);
if (updateRender)
UpdateRender(action);
}
/// <summary>
/// Wrapper to call a function on the specified xforms, then optionally add the requested action to the rendering queue.
/// If no xforms are selected via the checkboxes, and the update type is UPDATE_SELECTED, then the function will be called only on the currently selected xform.
/// If the update type is UPDATE_CURRENT_AND_SELECTED, and the current is not among those selected, then the function will be called on the currently selected xform as well.
/// </summary>
/// <param name="func">The function to call which will pass the xform under consideration, the absolute xform index, and the index within the selected xforms</param>
/// <param name="updateType">Whether to apply this update operation on the current, all or selected xforms. Default: eXformUpdate::UPDATE_CURRENT.</param>
/// <param name="updateRender">True to update renderer, else false. Default: true.</param>
/// <param name="action">The action to add to the rendering queue. Default: eProcessAction::FULL_RENDER.</param>
/// <param name="index">The xform index to use when action is eXformUpdate::UPDATE_SPECIFIC. Default: 0.</param>
template <typename T>
void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*, size_t, size_t)> func, eXformUpdate updateType, bool updateRender, eProcessAction action, size_t index)
{
int i = 0;
size_t selIndex = 0;
const auto current = CurrentXform();
const auto currentIndex = m_Fractorium->ui.CurrentXformCombo->currentIndex();
const bool forceFinal = m_Fractorium->HaveFinal();
const bool isCurrentFinal = m_Ember.IsFinalXform(current);
const bool doFinal = updateType != eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL && updateType != eXformUpdate::UPDATE_ALL_EXCEPT_FINAL;
switch (updateType)
{
case eXformUpdate::UPDATE_SPECIFIC:
{
if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
func(xform, index, 0);
}
break;
case eXformUpdate::UPDATE_CURRENT:
{
if (current)
func(current, currentIndex, 0);
}
break;
case eXformUpdate::UPDATE_CURRENT_AND_SELECTED:
{
bool currentDone = false;
while (const auto xform = m_Ember.GetTotalXform(i, forceFinal))
{
if (m_Fractorium->IsXformSelected(i))
{
func(xform, i, selIndex++);
if (xform == current)
currentDone = true;
}
i++;
}
if (!currentDone)//Current was not among those selected, so apply to it.
func(current, currentIndex, selIndex);
}
break;
case eXformUpdate::UPDATE_SELECTED:
case eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL:
{
bool anyUpdated = false;
while (const auto xform = (doFinal ? m_Ember.GetTotalXform(i, forceFinal) : m_Ember.GetXform(i)))
{
if (m_Fractorium->IsXformSelected(i))
{
func(xform, i, selIndex++);
anyUpdated = true;
}
i++;
}
if (!anyUpdated)//None were selected, so just apply to the current.
if (doFinal || !isCurrentFinal)//If do final, call func regardless. If not, only call if current is not final.
if (current)
func(current, currentIndex, selIndex);
}
break;
case eXformUpdate::UPDATE_ALL:
{
while (const auto xform = m_Ember.GetTotalXform(i, forceFinal))
func(xform, i++, selIndex++);
}
break;
case eXformUpdate::UPDATE_ALL_EXCEPT_FINAL:
default:
{
while (const auto xform = m_Ember.GetXform(i))
func(xform, i++, selIndex++);
}
break;
}
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>
/// <param name="verbatim">If true, do not overwrite temporal samples, quality or supersample value, else overwrite.</param>
/// <param name="updatePointer">If true, update the current ember pointer to the address of the one passed in.</param>
template <typename T>
template <typename U>
void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool verbatim, bool updatePointer)
{
if (ember.m_Name != m_Ember.m_Name)
m_LastSaveCurrent = "";
const auto w = m_Ember.m_FinalRasW;//Cache values for use below.
const auto h = m_Ember.m_FinalRasH;
m_Ember = ember;
if (updatePointer && (typeid(T) == typeid(U)))
m_EmberFilePointer = (Ember<T>*)&ember;
if (!verbatim)
{
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();
}
static EmberToXml<T> writer;//Save parameters of last full render just in case there is a crash.
const auto path = GetDefaultUserPath();
const QDir dir(path);
if (!dir.exists())
dir.mkpath(".");
const auto filename = path.toStdString() + "/last.flame";
writer.Save(filename, m_Ember, 0, true, true, false, true, true);
m_GLController->ResetMouseState();
FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo.
FillParamTablesAndPalette();
FillCurvesControl();
FillSummary();
//If a resize happened, this won't do anything because the new size is not reflected in the scroll area yet.
//However, it will have been taken care of in SyncSizes() in that case, so it's ok.
//This is for when a new ember with the same size was loaded. If it was larger than the scroll area, and was scrolled, re-center it.
if (m_Ember.m_FinalRasW == w && m_Ember.m_FinalRasH == h)
m_Fractorium->CenterScrollbars();
}
/// <summary>
/// Thin derivation to handle preview rendering multiple embers previews to a tree.
/// </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 TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
{
const auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
// Possible animate item at index 0
if (const auto top = m_Tree->topLevelItem(m_Tree->topLevelItemCount() - 1))
{
size_t i = start;
for (auto b = Advance(m_EmberFile.m_Embers.begin(), start); m_PreviewRun && i < end && b != m_EmberFile.m_Embers.end(); ++b, ++i)
{
m_PreviewEmber = *b;
m_PreviewEmber.SyncSize();
m_PreviewEmber.SetSizeAndAdjustScale(PREVIEW_SIZE, PREVIEW_SIZE, false, eScaleType::SCALE_WIDTH);
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_Quality = 25;
m_PreviewEmber.m_Supersample = 1;
m_PreviewRenderer.SetEmber(m_PreviewEmber, eProcessAction::FULL_RENDER, true);
if (m_PreviewRenderer.Run(m_PreviewFinalImage) == eRenderStatus::RENDER_OK)
{
if (const auto treeItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(int(i))))
{
//It is critical that Qt::DirectConnection 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.
if (m_PreviewRun)
{
QMetaObject::invokeMethod(f, "SetTreeItemData", Qt::DirectConnection,
Q_ARG(EmberTreeWidgetItemBase*, treeItem),
Q_ARG(vv4F&, m_PreviewFinalImage),
Q_ARG(uint, PREVIEW_SIZE),
Q_ARG(uint, PREVIEW_SIZE));
treeItem->SetRendered();
}
}
}
}
}
}
template class FractoriumEmberController<float>;
template class PreviewRenderer<float>;
template class TreePreviewRenderer<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
template class PreviewRenderer<double>;
template class TreePreviewRenderer<double>;
#endif
#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)
: m_PaletteList(PaletteList<float>::Instance())
{
Timing t;
m_Fractorium = fractorium;
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 = make_unique<QTimer>(m_Fractorium);
m_RenderTimer->setInterval(0);
m_Fractorium->connect(m_RenderTimer.get(), SIGNAL(timeout()), SLOT(IdleTimer()));
m_RenderRestartTimer = make_unique<QTimer>(m_Fractorium);
m_AnimateTimer = make_unique<QTimer>(m_Fractorium);
m_AnimateTimer->stop();
m_Fractorium->connect(m_RenderRestartTimer.get(), &QTimer::timeout, [&]() { m_Fractorium->StartRenderTimer(false); });//It's ok to pass false for the first shot because creating the controller will start the preview renders.
// XXX: why not SLOT(SequenceAnimateNextFrame())?
m_Fractorium->connect(m_AnimateTimer.get(), &QTimer::timeout, [&]() { SequenceAnimateNextFrame(); });
}
/// <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);
m_RenderTimer->stop();
m_RenderRestartTimer->stop();
m_AnimateTimer->stop();
}
/// <summary>
/// Pause or resume the renderer.
/// </summary>
/// <param name="pause">True to pause, false to unpause.</param>
void FractoriumEmberControllerBase::Pause(bool pause)
{
m_Renderer->Pause(pause);
}
/// <summary>
/// Retrieve the paused state of the renderer.
/// </summary>
/// <returns>True if the renderer is paused, else false.</returns>
bool FractoriumEmberControllerBase::Paused()
{
return m_Renderer->Paused();
}
/// <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_VariationList(VariationList<T>::Instance())
{
size_t b = 0;
m_GLController = make_unique<GLEmberController<T>>(fractorium, fractorium->ui.GLDisplay, this);
m_LibraryPreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.LibraryTree, m_EmberFile);
m_SequencePreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.SequenceTree, m_SequenceFile);
m_PaletteList->Clear();
m_Fractorium->ui.PaletteFilenameCombo->clear();
//Initial combo change event to fill the palette table will be called automatically later.
//Look hard for a palette.
const auto paths = GetDefaultPaths();
for (auto& path : paths)
b |= InitPaletteList(path);
if (b)
{
m_SheepTools = make_unique<SheepTools<T, float>>(m_PaletteList->Name(0), new EmberNs::Renderer<T, float>());
}
else
{
QString allPaths;
for (auto& path : paths)
allPaths += path + "\r\n";
allPaths = QString("No palettes found in paths:\r\n") + allPaths + "\r\nExiting.";
std::runtime_error ex(allPaths.toStdString());
throw ex;
}
if (m_PaletteList->Size() >= 1)//Only add the user palette if the folder already had a palette, which means we'll be using this folder.
if (m_PaletteList->AddEmptyPaletteFile((GetDefaultUserPath() + "/user-palettes.xml").toStdString()))
m_Fractorium->ui.PaletteFilenameCombo->addItem("user-palettes.xml");
BackgroundChanged(QColor(0, 0, 0));//Default to black.
ClearUndo();
}
/// <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, bool updatePointer) { SetEmberPrivate<float>(ember, verbatim, updatePointer); }
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation)
{
if (sequence)
{
emberFile.m_Filename = m_SequenceFile.m_Filename;
CopyCont(emberFile.m_Embers, m_SequenceFile.m_Embers, perEmberOperation);
}
else
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
}
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, bool updatePointer) { SetEmberPrivate<double>(ember, verbatim, updatePointer); }
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation)
{
if (sequence)
{
emberFile.m_Filename = m_SequenceFile.m_Filename;
CopyCont(emberFile.m_Embers, m_SequenceFile.m_Embers, perEmberOperation);
}
else
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
}
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; }
template <typename T>
void FractoriumEmberController<T>::ConstrainDimensions(Ember<T>& ember)
{
ember.m_FinalRasW = std::min<int>(m_Fractorium->ui.GLDisplay->MaxTexSize(), int(ember.m_FinalRasW));
ember.m_FinalRasH = std::min<int>(m_Fractorium->ui.GLDisplay->MaxTexSize(), int(ember.m_FinalRasH));
}
/// <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>
/// <param name="verbatim">If true, do not overwrite temporal samples, quality or supersample value, else overwrite.</param>
template <typename T>
void FractoriumEmberController<T>::SetEmber(size_t index, bool verbatim)
{
if (index < m_EmberFile.Size())
{
m_Fractorium->SelectLibraryItem(index);
ClearUndo();
SetEmber(*m_EmberFile.Get(index), verbatim, true);
}
}
/// <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: true.</param>
/// <param name="action">The action to add to the rendering queue. Default: eProcessAction::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 ember and optionally all other embers in the file.
/// 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: true.</param>
/// <param name="action">The action to add to the rendering queue. Default: eProcessAction::FULL_RENDER.</param>
/// <param name="applyAll">True to apply the action to all embers in the file in addition to the curent one, false to apply the action only to the current one.</param>
template <typename T>
void FractoriumEmberController<T>::UpdateAll(std::function<void(Ember<T>& ember, bool isMain)> func, bool updateRender, eProcessAction action, bool applyAll)
{
func(m_Ember, true);
if (applyAll)
for (auto& it : m_EmberFile.m_Embers)
func(it, false);
if (updateRender)
UpdateRender(action);
}
/// <summary>
/// Wrapper to call a function on the specified xforms, then optionally add the requested action to the rendering queue.
/// If no xforms are selected via the checkboxes, and the update type is UPDATE_SELECTED, then the function will be called only on the currently selected xform.
/// If the update type is UPDATE_CURRENT_AND_SELECTED, and the current is not among those selected, then the function will be called on the currently selected xform as well.
/// </summary>
/// <param name="func">The function to call which will pass the xform under consideration, the absolute xform index, and the index within the selected xforms</param>
/// <param name="updateType">Whether to apply this update operation on the current, all or selected xforms. Default: eXformUpdate::UPDATE_CURRENT.</param>
/// <param name="updateRender">True to update renderer, else false. Default: true.</param>
/// <param name="action">The action to add to the rendering queue. Default: eProcessAction::FULL_RENDER.</param>
/// <param name="index">The xform index to use when action is eXformUpdate::UPDATE_SPECIFIC. Default: 0.</param>
template <typename T>
void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*, size_t, size_t)> func, eXformUpdate updateType, bool updateRender, eProcessAction action, size_t index)
{
int i = 0;
size_t selIndex = 0;
const auto current = CurrentXform();
const auto currentIndex = m_Fractorium->ui.CurrentXformCombo->currentIndex();
const bool forceFinal = m_Fractorium->HaveFinal();
const bool isCurrentFinal = m_Ember.IsFinalXform(current);
const bool doFinal = updateType != eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL && updateType != eXformUpdate::UPDATE_ALL_EXCEPT_FINAL;
switch (updateType)
{
case eXformUpdate::UPDATE_SPECIFIC:
{
if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
func(xform, index, 0);
}
break;
case eXformUpdate::UPDATE_CURRENT:
{
if (current)
func(current, currentIndex, 0);
}
break;
case eXformUpdate::UPDATE_CURRENT_AND_SELECTED:
{
bool currentDone = false;
while (const auto xform = m_Ember.GetTotalXform(i, forceFinal))
{
if (m_Fractorium->IsXformSelected(i))
{
func(xform, i, selIndex++);
if (xform == current)
currentDone = true;
}
i++;
}
if (!currentDone)//Current was not among those selected, so apply to it.
func(current, currentIndex, selIndex);
}
break;
case eXformUpdate::UPDATE_SELECTED:
case eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL:
{
bool anyUpdated = false;
while (const auto xform = (doFinal ? m_Ember.GetTotalXform(i, forceFinal) : m_Ember.GetXform(i)))
{
if (m_Fractorium->IsXformSelected(i))
{
func(xform, i, selIndex++);
anyUpdated = true;
}
i++;
}
if (!anyUpdated)//None were selected, so just apply to the current.
if (doFinal || !isCurrentFinal)//If do final, call func regardless. If not, only call if current is not final.
if (current)
func(current, currentIndex, selIndex);
}
break;
case eXformUpdate::UPDATE_ALL:
{
while (const auto xform = m_Ember.GetTotalXform(i, forceFinal))
func(xform, i++, selIndex++);
}
break;
case eXformUpdate::UPDATE_ALL_EXCEPT_FINAL:
default:
{
while (const auto xform = m_Ember.GetXform(i))
func(xform, i++, selIndex++);
}
break;
}
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>
/// <param name="verbatim">If true, do not overwrite temporal samples, quality or supersample value, else overwrite.</param>
/// <param name="updatePointer">If true, update the current ember pointer to the address of the one passed in.</param>
template <typename T>
template <typename U>
void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool verbatim, bool updatePointer)
{
if (ember.m_Name != m_Ember.m_Name)
m_LastSaveCurrent = "";
const auto w = m_Ember.m_FinalRasW;//Cache values for use below.
const auto h = m_Ember.m_FinalRasH;
m_Ember = ember;
if (updatePointer && (typeid(T) == typeid(U)))
m_EmberFilePointer = (Ember<T>*)&ember;
if (!verbatim)
{
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();
}
static EmberToXml<T> writer;//Save parameters of last full render just in case there is a crash.
const auto path = GetDefaultUserPath();
const QDir dir(path);
if (!dir.exists())
dir.mkpath(".");
const auto filename = path.toStdString() + "/last.flame";
writer.Save(filename, m_Ember, 0, true, true, false, true, true);
m_GLController->ResetMouseState();
FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo.
FillParamTablesAndPalette();
FillCurvesControl();
FillSummary();
//If a resize happened, this won't do anything because the new size is not reflected in the scroll area yet.
//However, it will have been taken care of in SyncSizes() in that case, so it's ok.
//This is for when a new ember with the same size was loaded. If it was larger than the scroll area, and was scrolled, re-center it.
if (m_Ember.m_FinalRasW == w && m_Ember.m_FinalRasH == h)
m_Fractorium->CenterScrollbars();
}
/// <summary>
/// Thin derivation to handle preview rendering multiple embers previews to a tree.
/// </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 TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
{
const auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
// Possible animate item at index 0
if (const auto top = m_Tree->topLevelItem(m_Tree->topLevelItemCount() - 1))
{
size_t i = start;
for (auto b = Advance(m_EmberFile.m_Embers.begin(), start); m_PreviewRun && i < end && b != m_EmberFile.m_Embers.end(); ++b, ++i)
{
m_PreviewEmber = *b;
m_PreviewEmber.SyncSize();
m_PreviewEmber.SetSizeAndAdjustScale(PREVIEW_SIZE, PREVIEW_SIZE, false, eScaleType::SCALE_WIDTH);
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_Quality = 25;
m_PreviewEmber.m_Supersample = 1;
m_PreviewRenderer.SetEmber(m_PreviewEmber, eProcessAction::FULL_RENDER, true);
if (m_PreviewRenderer.Run(m_PreviewFinalImage) == eRenderStatus::RENDER_OK)
{
if (const auto treeItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(int(i))))
{
//It is critical that Qt::DirectConnection 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.
if (m_PreviewRun)
{
QMetaObject::invokeMethod(f, "SetTreeItemData", Qt::DirectConnection,
Q_ARG(EmberTreeWidgetItemBase*, treeItem),
Q_ARG(vv4F&, m_PreviewFinalImage),
Q_ARG(uint, PREVIEW_SIZE),
Q_ARG(uint, PREVIEW_SIZE));
treeItem->SetRendered();
}
}
}
}
}
}
template class FractoriumEmberController<float>;
template class PreviewRenderer<float>;
template class TreePreviewRenderer<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
template class PreviewRenderer<double>;
template class TreePreviewRenderer<double>;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,305 +1,305 @@
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the info UI.
/// </summary>
void Fractorium::InitInfoUI()
{
const auto treeHeader = ui.SummaryTree->header();
const auto tableHeader = ui.SummaryTable->horizontalHeader();
treeHeader->setVisible(true);
treeHeader->setSectionsClickable(true);
treeHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
connect(treeHeader, SIGNAL(sectionClicked(int)), this, SLOT(OnSummaryTreeHeaderSectionClicked(int)), Qt::QueuedConnection);
connect(tableHeader, SIGNAL(sectionResized(int, int, int)), this, SLOT(OnSummaryTableHeaderResized(int, int, int)), Qt::QueuedConnection);
SetFixedTableHeader(ui.SummaryTable->verticalHeader());
ui.SummaryTable->setItem(0, 0, m_InfoNameItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(1, 0, m_InfoPaletteItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(2, 0, m_Info3dItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(3, 0, m_InfoXaosItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(4, 0, m_InfoXformCountItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(5, 0, m_InfoFinalXformItem = new QTableWidgetItem(""));
ui.InfoTabWidget->setCurrentIndex(0);//Make summary tab focused by default.
ui.SummaryTree->SetMainWindow(this);
}
/// <summary>
/// Called when the palette cell of the summary table is resized in response
/// to a resizing of the Info dock.
/// </summary>
/// <param name="logicalIndex">Ignored</param>
/// <param name="oldSize">Ignored</param>
/// <param name="newSize">Ignored</param>
void Fractorium::OnSummaryTableHeaderResized(int logicalIndex, int oldSize, int newSize)
{
QPixmap pixmap(QPixmap::fromImage(m_Controller->FinalPaletteImage()));//Create a QPixmap out of the QImage, will be empty on startup.
SetPaletteTableItem(&pixmap, ui.SummaryTable, m_InfoPaletteItem, 1, 0);
}
/// <summary>
/// Expand or collapse the summary tree depending on the column index clicked.
/// 0: collapse, 1: expand.
/// </summary>
/// <param name="logicalIndex">The column which was clicked</param>
void Fractorium::OnSummaryTreeHeaderSectionClicked(int logicalIndex)
{
if (const auto tree = ui.SummaryTree)
{
if (logicalIndex)
tree->expandAll();
else
tree->collapseAll();
}
}
/// <summary>
/// Fill the summary tree with values from the current ember.
/// This is meant to be a rough summary by containing only the most relevant
/// values from the ember.
/// It's also meant to be used in a fire-and-forget way. Once the tree is filled
/// individual nodes are never referenced again.
/// The entire tree is cleared and refilled whenever a render is completed.
/// This would seem inefficient, but it appears to update with no flicker.
/// If this ever presents a problem in the future, revisit with a more
/// intelligent design.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillSummary()
{
const auto p = 3;
const auto vp = 4;
const auto vlen = 7;
const auto pc = 'f';
const auto forceFinal = m_Fractorium->HaveFinal();
const auto total = m_Ember.TotalXformCount(forceFinal);
const auto table = m_Fractorium->ui.SummaryTable;
const auto nondraggable = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
const auto draggable = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
size_t x = 0;
Xform<T>* xform = nullptr;
QColor color;
auto tree = m_Fractorium->ui.SummaryTree;
tree->blockSignals(true);
tree->clear();
m_Fractorium->m_InfoNameItem->setText(m_Ember.m_Name.c_str());
m_Fractorium->m_Info3dItem->setText(m_Ember.ProjBits() ? "Yes" : "No");
m_Fractorium->m_InfoXaosItem->setText(m_Ember.XaosPresent() ? "Yes" : "No");
m_Fractorium->m_InfoXformCountItem->setText(QString::number(m_Ember.XformCount()));
m_Fractorium->m_InfoFinalXformItem->setText(m_Ember.UseFinalXform() ? "Yes" : "No");
QPixmap pixmap(QPixmap::fromImage(m_FinalPaletteImage));//Create a QPixmap out of the QImage.
QSize size(table->columnWidth(0), table->rowHeight(1) + 1);
m_Fractorium->m_InfoPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
for (x = 0; x < total && (xform = m_Ember.GetTotalXform(x, forceFinal)); x++)
{
size_t i = 0;
QString as = "Pre";
auto item1 = new QTreeWidgetItem(tree);
intmax_t linkedIndex = IsXformLinked(m_Ember, xform);
QString linked = (linkedIndex != -1) ? (" Linked to " + QString::number(linkedIndex + 1)) : "";
auto index = m_Ember.GetXformIndex(xform);
m_Ember.CalcNormalizedWeights(m_NormalizedWeights);
xform->SetPrecalcFlags();//Needed for HasPost() below.
if (!m_Ember.IsFinalXform(xform) && index != -1)
{
item1->setText(0, "Xform " +
QString::number(x + 1) +
" (" + QLocale::system().toString(xform->m_Weight, pc, p) + ") (" +
QLocale::system().toString(double(m_NormalizedWeights[index]), pc, p) + ")" +
linked);
}
else
item1->setText(0, "Final xform");
item1->setText(1, xform->m_Name.c_str());
item1->setFlags(nondraggable);
auto affineItem = new QTreeWidgetItem(item1);
affineItem->setText(0, "Affine");
affineItem->setFlags(nondraggable);
if (xform->m_Affine.IsZero())
as += " Empty";
else if (xform->m_Affine.IsID())
as += " ID";
if (xform->HasPost())
{
as += ", Post";
if (xform->m_Post.IsZero())
as += " Empty";//Don't need to check further for IsID() because post is not included if it's ID.
}
affineItem->setText(1, as);
auto colorIndexItem = new QTreeWidgetItem(item1);
colorIndexItem->setText(0, "Color index");
colorIndexItem->setText(1, QLocale::system().toString(xform->m_ColorX, pc, p));
colorIndexItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
color = ColorIndexToQColor(xform->m_ColorX);
color.setAlphaF(xform->m_Opacity);
colorIndexItem->setBackgroundColor(1, color);
colorIndexItem->setTextColor(1, VisibleColor(color));
auto colorSpeedItem = new QTreeWidgetItem(item1);
colorSpeedItem->setText(0, "Color speed");
colorSpeedItem->setText(1, QLocale::system().toString(xform->m_ColorSpeed, pc, p));
colorSpeedItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
auto opacityItem = new QTreeWidgetItem(item1);
opacityItem->setText(0, "Opacity");
opacityItem->setText(1, QLocale::system().toString(xform->m_Opacity, pc, p));
opacityItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
auto dcItem = new QTreeWidgetItem(item1);
dcItem->setText(0, "Direct color");
dcItem->setText(1, QLocale::system().toString(xform->m_DirectColor, pc, p));
dcItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
if (dcItem->text(0) != tree->LastNonVarField())
throw "Last info tree non-variation index did not match expected value";
while (auto var = xform->GetVariation(i++))
{
auto vitem = new VariationTreeWidgetItem(var->VariationId(), item1);
vitem->setText(0, QString::fromStdString(var->Name()));
vitem->setText(1, QLocale::system().toString(var->m_Weight, pc, vp).rightJustified(vlen, ' '));
vitem->setFlags(draggable);
if (const auto parVar = dynamic_cast<ParametricVariation<T>*>(var))
{
auto params = parVar->Params();
for (auto j = 0; j < parVar->ParamCount(); j++)
{
if (!params[j].IsPrecalc())
{
auto pitem = new QTreeWidgetItem(vitem);
pitem->setText(0, params[j].Name().c_str());
pitem->setText(1, QLocale::system().toString(params[j].ParamVal(), pc, vp).rightJustified(vlen, ' '));
pitem->setFlags(nondraggable);
}
}
}
}
const auto item2 = new QTreeWidgetItem(tree);//Empty item in between xforms.
}
tree->expandAll();
tree->blockSignals(false);
}
void Fractorium::FillSummary()
{
m_Controller->FillSummary();
}
/// <summary>
/// Reorder the variations of the xform for the passed in tree widget item.
/// Read the newly reordered variation items in order, removing each from the xform
/// corresponding to the passed in item, and storing them in a vector. Then re-add those variation
/// pointers back to the xform in the same order they were removed.
/// This will be called after the user performs a drag and drop operation on the variations in the
/// info tree. So the variations will be in the newly desired order.
/// </summary>
/// <param name="dme">Pointer to the parent (xform level) tree widget item which contains the variation item being dragged</param>
template <typename T>
void FractoriumEmberController<T>::ReorderVariations(QTreeWidgetItem* item)
{
const auto tree = m_Fractorium->ui.SummaryTree;
const auto xfindex = tree->indexOfTopLevelItem(item) / 2;//Blank lines each count as one.
if (auto xform = m_Ember.GetTotalXform(xfindex))
{
vector<Variation<T>*> vars;
vars.reserve(xform->TotalVariationCount());
Update([&]
{
int i = 0;
while (const auto ch = item->child(i))
{
if (ch->text(0) == tree->LastNonVarField())
{
i++;
while (auto varch = dynamic_cast<VariationTreeWidgetItem*>(item->child(i++)))
if (auto var = xform->RemoveVariationById(varch->Id()))
vars.push_back(var);
for (auto& var : vars)
xform->AddVariation(var);
break;
}
i++;
}
}, true, eProcessAction::FULL_RENDER);
}
}
void Fractorium::ReorderVariations(QTreeWidgetItem* item)
{
m_Controller->ReorderVariations(item);
}
/// <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()
{
static QString ul, ur, lr, ll, wh, g, de;
if (auto r = m_Controller->Renderer())
{
ul.sprintf("UL: %3.3f, %3.3f", r->LowerLeftX(), r->UpperRightY());//These bounds include gutter padding.
ur.sprintf("UR: %3.3f, %3.3f", r->UpperRightX(), r->UpperRightY());
lr.sprintf("LR: %3.3f, %3.3f", r->UpperRightX(), r->LowerLeftY());
ll.sprintf("LL: %3.3f, %3.3f", r->LowerLeftX(), r->LowerLeftY());
wh.sprintf("W x H: %4u x %4u", r->SuperRasW(), r->SuperRasH());
g.sprintf("%u", static_cast<uint>(r->GutterWidth()));
ui.InfoBoundsLabelUL->setText(ul);
ui.InfoBoundsLabelUR->setText(ur);
ui.InfoBoundsLabelLR->setText(lr);
ui.InfoBoundsLabelLL->setText(ll);
ui.InfoBoundsLabelWH->setText(wh);
ui.InfoBoundsTable->item(0, 1)->setText(g);
if (r->GetDensityFilter())
{
const auto deWidth = (r->GetDensityFilter()->FilterWidth() * 2) + 1;
de.sprintf("%d x %d", deWidth, deWidth);
ui.InfoBoundsTable->item(1, 1)->setText(de);
}
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(const vector<string>& errors, QTextEdit* textEdit, bool clear)
{
if (clear)
QMetaObject::invokeMethod(textEdit, "clear", Qt::QueuedConnection);
for (auto& error : errors)
QMetaObject::invokeMethod(textEdit, "append", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(error) + "\n"));
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the info UI.
/// </summary>
void Fractorium::InitInfoUI()
{
const auto treeHeader = ui.SummaryTree->header();
const auto tableHeader = ui.SummaryTable->horizontalHeader();
treeHeader->setVisible(true);
treeHeader->setSectionsClickable(true);
treeHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
connect(treeHeader, SIGNAL(sectionClicked(int)), this, SLOT(OnSummaryTreeHeaderSectionClicked(int)), Qt::QueuedConnection);
connect(tableHeader, SIGNAL(sectionResized(int, int, int)), this, SLOT(OnSummaryTableHeaderResized(int, int, int)), Qt::QueuedConnection);
SetFixedTableHeader(ui.SummaryTable->verticalHeader());
ui.SummaryTable->setItem(0, 0, m_InfoNameItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(1, 0, m_InfoPaletteItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(2, 0, m_Info3dItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(3, 0, m_InfoXaosItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(4, 0, m_InfoXformCountItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(5, 0, m_InfoFinalXformItem = new QTableWidgetItem(""));
ui.InfoTabWidget->setCurrentIndex(0);//Make summary tab focused by default.
ui.SummaryTree->SetMainWindow(this);
}
/// <summary>
/// Called when the palette cell of the summary table is resized in response
/// to a resizing of the Info dock.
/// </summary>
/// <param name="logicalIndex">Ignored</param>
/// <param name="oldSize">Ignored</param>
/// <param name="newSize">Ignored</param>
void Fractorium::OnSummaryTableHeaderResized(int logicalIndex, int oldSize, int newSize)
{
QPixmap pixmap(QPixmap::fromImage(m_Controller->FinalPaletteImage()));//Create a QPixmap out of the QImage, will be empty on startup.
SetPaletteTableItem(&pixmap, ui.SummaryTable, m_InfoPaletteItem, 1, 0);
}
/// <summary>
/// Expand or collapse the summary tree depending on the column index clicked.
/// 0: collapse, 1: expand.
/// </summary>
/// <param name="logicalIndex">The column which was clicked</param>
void Fractorium::OnSummaryTreeHeaderSectionClicked(int logicalIndex)
{
if (const auto tree = ui.SummaryTree)
{
if (logicalIndex)
tree->expandAll();
else
tree->collapseAll();
}
}
/// <summary>
/// Fill the summary tree with values from the current ember.
/// This is meant to be a rough summary by containing only the most relevant
/// values from the ember.
/// It's also meant to be used in a fire-and-forget way. Once the tree is filled
/// individual nodes are never referenced again.
/// The entire tree is cleared and refilled whenever a render is completed.
/// This would seem inefficient, but it appears to update with no flicker.
/// If this ever presents a problem in the future, revisit with a more
/// intelligent design.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillSummary()
{
const auto p = 3;
const auto vp = 4;
const auto vlen = 7;
const auto pc = 'f';
const auto forceFinal = m_Fractorium->HaveFinal();
const auto total = m_Ember.TotalXformCount(forceFinal);
const auto table = m_Fractorium->ui.SummaryTable;
const auto nondraggable = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
const auto draggable = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
size_t x = 0;
Xform<T>* xform = nullptr;
QColor color;
auto tree = m_Fractorium->ui.SummaryTree;
tree->blockSignals(true);
tree->clear();
m_Fractorium->m_InfoNameItem->setText(m_Ember.m_Name.c_str());
m_Fractorium->m_Info3dItem->setText(m_Ember.ProjBits() ? "Yes" : "No");
m_Fractorium->m_InfoXaosItem->setText(m_Ember.XaosPresent() ? "Yes" : "No");
m_Fractorium->m_InfoXformCountItem->setText(QString::number(m_Ember.XformCount()));
m_Fractorium->m_InfoFinalXformItem->setText(m_Ember.UseFinalXform() ? "Yes" : "No");
QPixmap pixmap(QPixmap::fromImage(m_FinalPaletteImage));//Create a QPixmap out of the QImage.
QSize size(table->columnWidth(0), table->rowHeight(1) + 1);
m_Fractorium->m_InfoPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
for (x = 0; x < total && (xform = m_Ember.GetTotalXform(x, forceFinal)); x++)
{
size_t i = 0;
QString as = "Pre";
auto item1 = new QTreeWidgetItem(tree);
intmax_t linkedIndex = IsXformLinked(m_Ember, xform);
QString linked = (linkedIndex != -1) ? (" Linked to " + QString::number(linkedIndex + 1)) : "";
auto index = m_Ember.GetXformIndex(xform);
m_Ember.CalcNormalizedWeights(m_NormalizedWeights);
xform->SetPrecalcFlags();//Needed for HasPost() below.
if (!m_Ember.IsFinalXform(xform) && index != -1)
{
item1->setText(0, "Xform " +
QString::number(x + 1) +
" (" + QLocale::system().toString(xform->m_Weight, pc, p) + ") (" +
QLocale::system().toString(double(m_NormalizedWeights[index]), pc, p) + ")" +
linked);
}
else
item1->setText(0, "Final xform");
item1->setText(1, xform->m_Name.c_str());
item1->setFlags(nondraggable);
auto affineItem = new QTreeWidgetItem(item1);
affineItem->setText(0, "Affine");
affineItem->setFlags(nondraggable);
if (xform->m_Affine.IsZero())
as += " Empty";
else if (xform->m_Affine.IsID())
as += " ID";
if (xform->HasPost())
{
as += ", Post";
if (xform->m_Post.IsZero())
as += " Empty";//Don't need to check further for IsID() because post is not included if it's ID.
}
affineItem->setText(1, as);
auto colorIndexItem = new QTreeWidgetItem(item1);
colorIndexItem->setText(0, "Color index");
colorIndexItem->setText(1, QLocale::system().toString(xform->m_ColorX, pc, p));
colorIndexItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
color = ColorIndexToQColor(xform->m_ColorX);
color.setAlphaF(xform->m_Opacity);
colorIndexItem->setBackground(1, color);
colorIndexItem->setForeground(1, VisibleColor(color));
auto colorSpeedItem = new QTreeWidgetItem(item1);
colorSpeedItem->setText(0, "Color speed");
colorSpeedItem->setText(1, QLocale::system().toString(xform->m_ColorSpeed, pc, p));
colorSpeedItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
auto opacityItem = new QTreeWidgetItem(item1);
opacityItem->setText(0, "Opacity");
opacityItem->setText(1, QLocale::system().toString(xform->m_Opacity, pc, p));
opacityItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
auto dcItem = new QTreeWidgetItem(item1);
dcItem->setText(0, "Direct color");
dcItem->setText(1, QLocale::system().toString(xform->m_DirectColor, pc, p));
dcItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
if (dcItem->text(0) != tree->LastNonVarField())
throw "Last info tree non-variation index did not match expected value";
while (auto var = xform->GetVariation(i++))
{
auto vitem = new VariationTreeWidgetItem(var->VariationId(), item1);
vitem->setText(0, QString::fromStdString(var->Name()));
vitem->setText(1, QLocale::system().toString(var->m_Weight, pc, vp).rightJustified(vlen, ' '));
vitem->setFlags(draggable);
if (const auto parVar = dynamic_cast<ParametricVariation<T>*>(var))
{
auto params = parVar->Params();
for (auto j = 0; j < parVar->ParamCount(); j++)
{
if (!params[j].IsPrecalc())
{
auto pitem = new QTreeWidgetItem(vitem);
pitem->setText(0, params[j].Name().c_str());
pitem->setText(1, QLocale::system().toString(params[j].ParamVal(), pc, vp).rightJustified(vlen, ' '));
pitem->setFlags(nondraggable);
}
}
}
}
const auto item2 = new QTreeWidgetItem(tree);//Empty item in between xforms.
}
tree->expandAll();
tree->blockSignals(false);
}
void Fractorium::FillSummary()
{
m_Controller->FillSummary();
}
/// <summary>
/// Reorder the variations of the xform for the passed in tree widget item.
/// Read the newly reordered variation items in order, removing each from the xform
/// corresponding to the passed in item, and storing them in a vector. Then re-add those variation
/// pointers back to the xform in the same order they were removed.
/// This will be called after the user performs a drag and drop operation on the variations in the
/// info tree. So the variations will be in the newly desired order.
/// </summary>
/// <param name="dme">Pointer to the parent (xform level) tree widget item which contains the variation item being dragged</param>
template <typename T>
void FractoriumEmberController<T>::ReorderVariations(QTreeWidgetItem* item)
{
const auto tree = m_Fractorium->ui.SummaryTree;
const auto xfindex = tree->indexOfTopLevelItem(item) / 2;//Blank lines each count as one.
if (auto xform = m_Ember.GetTotalXform(xfindex))
{
vector<Variation<T>*> vars;
vars.reserve(xform->TotalVariationCount());
Update([&]
{
int i = 0;
while (const auto ch = item->child(i))
{
if (ch->text(0) == tree->LastNonVarField())
{
i++;
while (auto varch = dynamic_cast<VariationTreeWidgetItem*>(item->child(i++)))
if (auto var = xform->RemoveVariationById(varch->Id()))
vars.push_back(var);
for (auto& var : vars)
xform->AddVariation(var);
break;
}
i++;
}
}, true, eProcessAction::FULL_RENDER);
}
}
void Fractorium::ReorderVariations(QTreeWidgetItem* item)
{
m_Controller->ReorderVariations(item);
}
/// <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()
{
static QString ul, ur, lr, ll, wh, g, de;
if (auto r = m_Controller->Renderer())
{
auto ulstr = ul.asprintf("UL: %3.3f, %3.3f", r->LowerLeftX(), r->UpperRightY());//These bounds include gutter padding.
auto urstr = ur.asprintf("UR: %3.3f, %3.3f", r->UpperRightX(), r->UpperRightY());
auto lrstr = lr.asprintf("LR: %3.3f, %3.3f", r->UpperRightX(), r->LowerLeftY());
auto llstr = ll.asprintf("LL: %3.3f, %3.3f", r->LowerLeftX(), r->LowerLeftY());
auto whstr = wh.asprintf("W x H: %4u x %4u", r->SuperRasW(), r->SuperRasH());
auto gstr = g.asprintf("%u", static_cast<uint>(r->GutterWidth()));
ui.InfoBoundsLabelUL->setText(ulstr);
ui.InfoBoundsLabelUR->setText(urstr);
ui.InfoBoundsLabelLR->setText(lrstr);
ui.InfoBoundsLabelLL->setText(llstr);
ui.InfoBoundsLabelWH->setText(whstr);
ui.InfoBoundsTable->item(0, 1)->setText(gstr);
if (r->GetDensityFilter())
{
const auto deWidth = (r->GetDensityFilter()->FilterWidth() * 2) + 1;
auto destr = de.asprintf("%d x %d", deWidth, deWidth);
ui.InfoBoundsTable->item(1, 1)->setText(destr);
}
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(const vector<string>& errors, QTextEdit* textEdit, bool clear)
{
if (clear)
QMetaObject::invokeMethod(textEdit, "clear", Qt::QueuedConnection);
for (auto& error : errors)
QMetaObject::invokeMethod(textEdit, "append", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(error) + "\n"));
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
#include "FractoriumPch.h"
#include "FractoriumPch.h"

View File

@ -1,138 +1,138 @@
#ifndef FRACTORIUM_PCH_H
#define FRACTORIUM_PCH_H//GCC doesn't like #pragma once
#define XFORM_COLOR_COUNT 14
#ifdef _WIN32
#pragma warning(disable : 4251; disable : 4661; disable : 4100)
#endif
//Has to come first on non-Windows platforms due to some weird naming collisions on *nix.
#ifndef _WIN32
#include <QtWidgets>
#endif
#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"
#ifdef _WIN32
#include <QtWidgets>
#endif
#include <math.h>
#include <deque>
#include "qfunctions.h"
#include <QApplication>
#include <QBrush>
#include <QCheckBox>
#include <QClipboard>
#include <QColor>
#include <QColorDialog>
#include <QComboBox>
#include <QConicalGradient>
#include <QDebug>
#include <QDesktopWidget>
#include <QDial>
#include <QDoubleSpinBox>
#include <QEvent>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFontDialog>
#include <QFontMetrics>
#include <QFrame>
#include <QFuture>
#include <QGraphicsView>
#include <QGridLayout>
#include <QGroupBox>
#include <QHash>
#include <QHBoxLayout>
#include <QIcon>
#include <QImage>
#include <QImageReader>
#include <QItemDelegate>
#include <QKeyEvent>
#include <QLabel>
#include <QLayout>
#include <QLinearGradient>
#include <QLineEdit>
#include <QMap>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QModelIndex>
#include <QMouseEvent>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QPainter>
#include <QPainterPath>
#include <QPaintEvent>
#include <QPixmap>
#include <QPoint>
#include <QPolygon>
#include <QPushButton>
#include <QRect>
#include <QResizeEvent>
#include <QSettings>
#include <QSignalMapper>
#include <QSize>
#include <QSizePolicy>
#include <QSpinBox>
#include <QStandardPaths>
#include <QtConcurrentRun>
#include <QtCore/qglobal.h>
#include <QtCore/QMultiHash>
#include <QtCore/QPair>
#include <QtCore/QSharedData>
#include <QtCore/QSize>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtCore/QVector>
#include <QTextEdit>
#include <QTextStream>
#include <QtGui/QFont>
#include <QtGui/QPalette>
#include <QtGui/QSyntaxHighlighter>
#include <QThread>
#include <QTime>
#include <QTimer>
#include <QToolBar>
#include <QToolTip>
#include <QTreeWidget>
#include <QtWidgets/QMainWindow>
#include <QVarLengthArray>
#include <QVBoxLayout>
#include <QVector>
#include <QWheelEvent>
#include <QWidget>
#include <QWidgetAction>
//#define GLM_FORCE_RADIANS 1
//#define GLM_ENABLE_EXPERIMENTAL 1
//#ifndef __APPLE__
// #define GLM_FORCE_INLINE 1
//#endif
//
//#include "glm/glm.hpp"
//#include "glm/gtc/matrix_transform.hpp"
//#include "glm/gtc/type_ptr.hpp"
#ifndef _WIN32
#undef Bool
#endif
using namespace std;
using namespace EmberNs;
using namespace EmberCLns;
using namespace EmberCommon;
#endif
#ifndef FRACTORIUM_PCH_H
#define FRACTORIUM_PCH_H//GCC doesn't like #pragma once
#define XFORM_COLOR_COUNT 14
#ifdef _WIN32
#pragma warning(disable : 4251; disable : 4661; disable : 4100)
#endif
//Has to come first on non-Windows platforms due to some weird naming collisions on *nix.
#ifndef _WIN32
#include <QtWidgets>
#endif
#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"
#ifdef _WIN32
#include <QtWidgets>
#endif
#include <math.h>
#include <deque>
#include "qfunctions.h"
#include <QApplication>
#include <QBrush>
#include <QCheckBox>
#include <QClipboard>
#include <QColor>
#include <QColorDialog>
#include <QComboBox>
#include <QConicalGradient>
#include <QDebug>
#include <QDial>
#include <QDoubleSpinBox>
#include <QEvent>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFontDialog>
#include <QFontMetrics>
#include <QFrame>
#include <QFuture>
#include <QGraphicsView>
#include <QGridLayout>
#include <QGroupBox>
#include <QHash>
#include <QHBoxLayout>
#include <QIcon>
#include <QImage>
#include <QImageReader>
#include <QItemDelegate>
#include <QKeyEvent>
#include <QLabel>
#include <QLayout>
#include <QLinearGradient>
#include <QLineEdit>
#include <QMap>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QModelIndex>
#include <QMouseEvent>
#include <QOpenGLFunctions>
#include <QtOpenGLWidgets/QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QPainter>
#include <QPainterPath>
#include <QPaintEvent>
#include <QPixmap>
#include <QPoint>
#include <QPolygon>
#include <QPushButton>
#include <QRect>
#include <QResizeEvent>
#include <QSettings>
#include <QSignalMapper>
#include <QSize>
#include <QSizePolicy>
#include <QSpinBox>
#include <QStandardPaths>
#include <QtConcurrentRun>
#include <QtCore/qglobal.h>
#include <QtCore/QMultiHash>
#include <QtCore/QPair>
#include <QtCore/QSharedData>
#include <QtCore/QSize>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtCore/QVector>
#include <QTextEdit>
#include <QTextStream>
#include <QtGui/QFont>
#include <QtGui/QPalette>
#include <QtGui/QSyntaxHighlighter>
#include <QThread>
#include <QTime>
#include <QTimer>
#include <QToolBar>
#include <QToolTip>
#include <QTreeWidget>
#include <QtWidgets/QMainWindow>
#include <QVarLengthArray>
#include <QVBoxLayout>
#include <QVector>
#include <QWheelEvent>
#include <QWidget>
#include <QWidgetAction>
//#define GLM_FORCE_RADIANS 1
//#define GLM_ENABLE_EXPERIMENTAL 1
//#ifndef __APPLE__
// #define GLM_FORCE_INLINE 1
//#endif
//
//#include "glm/glm.hpp"
//#include "glm/gtc/matrix_transform.hpp"
//#include "glm/gtc/type_ptr.hpp"
#ifndef _WIN32
#undef Bool
#endif
using namespace std;
using namespace EmberNs;
using namespace EmberCLns;
using namespace EmberCommon;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,398 +1,398 @@
#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>
FractoriumSettings::FractoriumSettings()
#ifdef _WIN32
: QSettings(QSettings::IniFormat, QSettings::UserScope, "Fractorium", "Fractorium", nullptr)
#else
: QSettings(QSettings::IniFormat, QSettings::UserScope, "fractorium", "fractorium", nullptr)
#endif
{
EnsureDefaults();
}
/// <summary>
/// Make sure options have reasonable values in them first.
/// </summary>
void FractoriumSettings::EnsureDefaults()
{
auto info = OpenCLInfo::Instance();
if (FinalQuality() == 0)
FinalQuality(1000);
if (FinalTemporalSamples() == 0)
FinalTemporalSamples(100);
if (FinalSupersample() == 0)
FinalSupersample(2);
if (FinalStrips() == 0)
FinalStrips(1);
if (XmlTemporalSamples() == 0)
XmlTemporalSamples(100);
if (XmlQuality() == 0)
XmlQuality(1000);
if (XmlSupersample() == 0)
XmlSupersample(2);
if (Devices().empty() && !info->Devices().empty())
Devices(QList<QVariant> { 0 });
if (ThreadCount() == 0 || ThreadCount() > Timing::ProcessorCount())
ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Default to one less to keep the UI responsive for first time users.
if (FinalThreadCount() == 0 || FinalThreadCount() > Timing::ProcessorCount())
FinalThreadCount(Timing::ProcessorCount());
FinalThreadPriority(Clamp<int>(FinalThreadPriority(), static_cast<int>(eThreadPriority::LOWEST), static_cast<int>(eThreadPriority::HIGHEST)));
CpuSubBatch(std::max(1u, CpuSubBatch()));
OpenCLSubBatch(std::max(1u, OpenCLSubBatch()));
if (OpenCLSubBatchPct() == 0)
OpenCLSubBatchPct(0.025);//Default to 256 iters per thread when using the default sub batch size of 10,240.
OpenCLSubBatchPct(Clamp(OpenCLSubBatchPct(), 0.01, 1.0));
if (FinalOpenCLSubBatchPct() == 0)
FinalOpenCLSubBatchPct(0.025);//Default to 256 iters per thread when using the default sub batch size of 10,240.
FinalOpenCLSubBatchPct(Clamp(FinalOpenCLSubBatchPct(), 0.01, 1.0));
RandomCount(std::max(1u, RandomCount()));
if (CpuQuality() == 0)
CpuQuality(10);
if (OpenClQuality() == 0)
OpenClQuality(30);
if (FinalScale() > static_cast<int>(eScaleType::SCALE_HEIGHT))
FinalScale(0);
if (OpenXmlExt() == "")
OpenXmlExt("*.flame");
if (SaveXmlExt() == "")
SaveXmlExt(".flame");
if (OpenImageExt() == "")
OpenImageExt("*.png");
if (SaveImageExt() == "")
SaveImageExt(".png");
if (FinalExt() != "jpg" && FinalExt() != "png" && FinalExt() != "exr"
#ifdef _WIN32
&& FinalExt() != "bmp"
#endif
)
FinalExt("png");
if (AnimationFps() == 0)
AnimationFps(30);
auto s = OpenFolder();
QDir dir(s);
if (s.isEmpty() || !dir.exists())
{
QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DesktopLocation);
if (!paths.empty())
OpenFolder(paths[0]);
}
s = SaveFolder();
dir = QDir(s);
if (s.isEmpty() || !dir.exists())
{
QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DesktopLocation);
if (!paths.empty())
SaveFolder(paths[0]);
}
if (OpenPaletteImageFolder() == "")
OpenPaletteImageFolder(QCoreApplication::applicationDirPath());
if (value(SHAREDTEXTURE).toString() == "")//Set this to true if the setting is missing because it only needs to be false for the rare system that has problems with shared textures.
SharedTexture(true);
Compat::m_Compat = Flam3Compat();
}
/// <summary>
/// Interactive renderer settings.
/// </summary>
bool FractoriumSettings::EarlyClip() { return value(EARLYCLIP).toBool(); }
void FractoriumSettings::EarlyClip(bool b) { setValue(EARLYCLIP, b); }
bool FractoriumSettings::YAxisUp() { return value(YAXISUP).toBool(); }
void FractoriumSettings::YAxisUp(bool b) { setValue(YAXISUP, b); }
bool FractoriumSettings::Transparency() { return value(TRANSPARENCY).toBool(); }
void FractoriumSettings::Transparency(bool b) { setValue(TRANSPARENCY, b); }
bool FractoriumSettings::OpenCL() { return value(OPENCL).toBool(); }
void FractoriumSettings::OpenCL(bool b) { setValue(OPENCL, b); }
bool FractoriumSettings::SharedTexture() { return value(SHAREDTEXTURE).toBool(); }
void FractoriumSettings::SharedTexture(bool b) { setValue(SHAREDTEXTURE, b); }
bool FractoriumSettings::Double() { return value(DOUBLEPRECISION).toBool(); }
void FractoriumSettings::Double(bool b) { setValue(DOUBLEPRECISION, b); }
bool FractoriumSettings::ShowAllXforms() { return value(SHOWALLXFORMS).toBool(); }
void FractoriumSettings::ShowAllXforms(bool b) { setValue(SHOWALLXFORMS, b); }
bool FractoriumSettings::ShowXforms() { return value(SHOWXFORMS, QVariant::fromValue(true)).toBool(); }
void FractoriumSettings::ShowXforms(bool b) { setValue(SHOWXFORMS, b); }
bool FractoriumSettings::ShowGrid() { return value(SHOWGRID, QVariant::fromValue(true)).toBool(); }
void FractoriumSettings::ShowGrid(bool b) { setValue(SHOWGRID, b); }
bool FractoriumSettings::ToggleType() { return value(TOGGLETYPE).toBool(); }
void FractoriumSettings::ToggleType(bool b) { setValue(TOGGLETYPE, b); }
bool FractoriumSettings::Png16Bit() { return value(PNG16BIT).toBool(); }
void FractoriumSettings::Png16Bit(bool b) { setValue(PNG16BIT, b); }
bool FractoriumSettings::ContinuousUpdate() { return value(CONTUPDATE).toBool(); }
void FractoriumSettings::ContinuousUpdate(bool b) { setValue(CONTUPDATE, b); }
QList<QVariant> FractoriumSettings::Devices() { return value(DEVICES).toList(); }
void FractoriumSettings::Devices(const QList<QVariant>& devices) { setValue(DEVICES, devices); }
uint FractoriumSettings::ThreadCount() { return value(THREADCOUNT).toUInt(); }
void FractoriumSettings::ThreadCount(uint 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); }
uint FractoriumSettings::CpuSubBatch() { return value(CPUSUBBATCH).toUInt(); }
void FractoriumSettings::CpuSubBatch(uint i) { setValue(CPUSUBBATCH, i); }
double FractoriumSettings::OpenCLSubBatchPct() { return value(OPENCLSUBBATCHPCT).toDouble(); }
void FractoriumSettings::OpenCLSubBatchPct(double d) { setValue(OPENCLSUBBATCHPCT, d); }
uint FractoriumSettings::OpenCLSubBatch() { return value(OPENCLSUBBATCH).toUInt(); }
void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH, i); }
uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); }
void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); }
uint FractoriumSettings::CpuQuality() { return value(CPUQUALITY).toUInt(); }
void FractoriumSettings::CpuQuality(uint i) { setValue(CPUQUALITY, i); }
uint FractoriumSettings::OpenClQuality() { return value(OPENCLQUALITY).toUInt(); }
void FractoriumSettings::OpenClQuality(uint i) { setValue(OPENCLQUALITY, i); }
bool FractoriumSettings::LoadLast() { return value(LOADLAST).toBool(); }
void FractoriumSettings::LoadLast(bool b) { setValue(LOADLAST, b); }
bool FractoriumSettings::RotateAndScale() { return value(ROTSCALE).toBool(); }
void FractoriumSettings::RotateAndScale(bool b) { setValue(ROTSCALE, b); }
bool FractoriumSettings::Flam3Compat() { return value(FLAM3COMPAT, true).toBool(); }
void FractoriumSettings::Flam3Compat(bool b) { setValue(FLAM3COMPAT, b); }
/// <summary>
/// Sequence generation settings.
/// </summary>
double FractoriumSettings::Stagger() { return value(STAGGER).toDouble(); }
void FractoriumSettings::Stagger(double d) { setValue(STAGGER, d); }
double FractoriumSettings::StaggerMax() { return value(STAGGERMAX).toDouble(); }
void FractoriumSettings::StaggerMax(double d) { setValue(STAGGERMAX, d); }
uint FractoriumSettings::FramesPerRot() { return value(FRAMESPERROT).toUInt(); }
void FractoriumSettings::FramesPerRot(uint i) { setValue(FRAMESPERROT, i); }
uint FractoriumSettings::FramesPerRotMax() { return value(FRAMESPERROTMAX).toUInt(); }
void FractoriumSettings::FramesPerRotMax(uint i) { setValue(FRAMESPERROTMAX, i); }
uint FractoriumSettings::Rotations() { return value(ROTATIONS).toDouble(); }
void FractoriumSettings::Rotations(double d) { setValue(ROTATIONS, d); }
uint FractoriumSettings::RotationsMax() { return value(ROTATIONSMAX).toDouble(); }
void FractoriumSettings::RotationsMax(double d) { setValue(ROTATIONSMAX, d); }
uint FractoriumSettings::BlendFrames() { return value(BLENDFRAMES).toUInt(); }
void FractoriumSettings::BlendFrames(uint i) { setValue(BLENDFRAMES, i); }
uint FractoriumSettings::BlendFramesMax() { return value(BLENDFRAMESMAX).toUInt(); }
void FractoriumSettings::BlendFramesMax(uint i) { setValue(BLENDFRAMESMAX, i); }
uint FractoriumSettings::RotationsPerBlend() { return value(ROTATIONSPERBLEND).toUInt(); }
void FractoriumSettings::RotationsPerBlend(uint i) { setValue(ROTATIONSPERBLEND, i); }
uint FractoriumSettings::RotationsPerBlendMax() { return value(ROTATIONSPERBLENDMAX).toUInt(); }
void FractoriumSettings::RotationsPerBlendMax(uint i) { setValue(ROTATIONSPERBLENDMAX, i); }
bool FractoriumSettings::Linear() { return value(LINEAR).toBool(); }
void FractoriumSettings::Linear(bool b) { setValue(LINEAR, b); }
uint FractoriumSettings::AnimationFps() { return value(ANIMATION_FPS).toUInt(); }
void FractoriumSettings::AnimationFps(uint i) { setValue(ANIMATION_FPS, i); }
/// <summary>
/// Variations filter settings.
/// </summary>
int FractoriumSettings::VarFilterSum() { return value(VARFILTERSUM).toInt(); }
void FractoriumSettings::VarFilterSum(int i) { setValue(VARFILTERSUM, i); }
int FractoriumSettings::VarFilterAssign() { return value(VARFILTERASSIGN).toInt(); }
void FractoriumSettings::VarFilterAssign(int i) { setValue(VARFILTERASSIGN, i); }
int FractoriumSettings::VarFilterPpsum() { return value(VARFILTERPPSUM).toInt(); }
void FractoriumSettings::VarFilterPpsum(int i) { setValue(VARFILTERPPSUM, i); }
int FractoriumSettings::VarFilterPpassign() { return value(VARFILTERPPASSIGN).toInt(); }
void FractoriumSettings::VarFilterPpassign(int i) { setValue(VARFILTERPPASSIGN, i); }
int FractoriumSettings::VarFilterSdc() { return value(VARFILTERSDC).toInt(); }
void FractoriumSettings::VarFilterSdc(int i) { setValue(VARFILTERSDC, i); }
int FractoriumSettings::VarFilterState() { return value(VARFILTERSTATE).toInt(); }
void FractoriumSettings::VarFilterState(int i) { setValue(VARFILTERSTATE, i); }
int FractoriumSettings::VarFilterParam() { return value(VARFILTERPARAM).toInt(); }
void FractoriumSettings::VarFilterParam(int i) { setValue(VARFILTERPARAM, i); }
int FractoriumSettings::VarFilterNonparam() { return value(VARFILTERNONPARAM).toInt(); }
void FractoriumSettings::VarFilterNonparam(int i) { setValue(VARFILTERNONPARAM, i); }
/// <summary>
/// Final render settings.
/// </summary>
bool FractoriumSettings::FinalEarlyClip() { return value(FINALEARLYCLIP).toBool(); }
void FractoriumSettings::FinalEarlyClip(bool b) { setValue(FINALEARLYCLIP, b); }
bool FractoriumSettings::FinalYAxisUp() { return value(FINALYAXISUP).toBool(); }
void FractoriumSettings::FinalYAxisUp(bool b) { setValue(FINALYAXISUP, 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::FinalPng16Bit() { return value(FINALPNG16BIT).toBool(); }
void FractoriumSettings::FinalPng16Bit(bool b) { setValue(FINALPNG16BIT, b); }
bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); }
void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); }
uint FractoriumSettings::FinalScale() { return value(FINALSCALE).toUInt(); }
void FractoriumSettings::FinalScale(uint i) { setValue(FINALSCALE, i); }
QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); }
void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); }
QList<QVariant> FractoriumSettings::FinalDevices() { return value(FINALDEVICES).toList(); }
void FractoriumSettings::FinalDevices(const QList<QVariant>& devices) { setValue(FINALDEVICES, devices); }
uint FractoriumSettings::FinalThreadCount() { return value(FINALTHREADCOUNT).toUInt(); }
void FractoriumSettings::FinalThreadCount(uint i) { setValue(FINALTHREADCOUNT, i); }
int FractoriumSettings::FinalThreadPriority() { return value(FINALTHREADPRIORITY).toInt(); }
void FractoriumSettings::FinalThreadPriority(int i) { setValue(FINALTHREADPRIORITY, i); }
double FractoriumSettings::FinalOpenCLSubBatchPct() { return value(FINALOPENCLSUBBATCHPCT).toDouble(); }
void FractoriumSettings::FinalOpenCLSubBatchPct(double d) { setValue(FINALOPENCLSUBBATCHPCT, d); }
uint FractoriumSettings::FinalQuality() { return value(FINALQUALITY).toUInt(); }
void FractoriumSettings::FinalQuality(uint i) { setValue(FINALQUALITY, i); }
uint FractoriumSettings::FinalTemporalSamples() { return value(FINALTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::FinalTemporalSamples(uint i) { setValue(FINALTEMPORALSAMPLES, i); }
uint FractoriumSettings::FinalSupersample() { return value(FINALSUPERSAMPLE).toUInt(); }
void FractoriumSettings::FinalSupersample(uint i) { setValue(FINALSUPERSAMPLE, i); }
size_t FractoriumSettings::FinalStrips() { return value(FINALSTRIPS).toULongLong(); }
void FractoriumSettings::FinalStrips(size_t i) { setValue(FINALSTRIPS, static_cast<uint>(i)); }
/// <summary>
/// Xml file saving settings.
/// </summary>
uint FractoriumSettings::XmlTemporalSamples() { return value(XMLTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::XmlTemporalSamples(uint i) { setValue(XMLTEMPORALSAMPLES, i); }
uint FractoriumSettings::XmlQuality() { return value(XMLQUALITY).toUInt(); }
void FractoriumSettings::XmlQuality(uint i) { setValue(XMLQUALITY, i); }
uint FractoriumSettings::XmlSupersample() { return value(XMLSUPERSAMPLE).toUInt(); }
void FractoriumSettings::XmlSupersample(uint i) { setValue(XMLSUPERSAMPLE, i); }
QString FractoriumSettings::Id() { return value(IDENTITYID).toString(); }
void FractoriumSettings::Id(const QString& s) { setValue(IDENTITYID, s); }
QString FractoriumSettings::Url() { return value(IDENTITYURL).toString(); }
void FractoriumSettings::Url(const QString& s) { setValue(IDENTITYURL, s); }
QString FractoriumSettings::Nick() { return value(IDENTITYNICK).toString(); }
void FractoriumSettings::Nick(const QString& s) { setValue(IDENTITYNICK, s); }
/// <summary>
/// General operations settings.
/// </summary>
QString FractoriumSettings::OpenFolder() { return value(OPENFOLDER).toString(); }
void FractoriumSettings::OpenFolder(const QString& s) { setValue(OPENFOLDER, s); }
QString FractoriumSettings::OpenPaletteImageFolder() { return value(OPENPALETTEIMAGEFOLDER).toString(); }
void FractoriumSettings::OpenPaletteImageFolder(const QString& s) { setValue(OPENPALETTEIMAGEFOLDER, s); }
QString FractoriumSettings::SaveFolder() { return value(SAVEFOLDER).toString(); }
void FractoriumSettings::SaveFolder(const QString& s) { setValue(SAVEFOLDER, s); }
QString FractoriumSettings::OpenXmlExt() { return value(OPENXMLEXT).toString(); }
void FractoriumSettings::OpenXmlExt(const QString& s) { setValue(OPENXMLEXT, s); }
QString FractoriumSettings::SaveXmlExt() { return value(SAVEXMLEXT).toString(); }
void FractoriumSettings::SaveXmlExt(const QString& s) { setValue(SAVEXMLEXT, s); }
QString FractoriumSettings::OpenImageExt() { return value(OPENIMAGEEXT).toString(); }
void FractoriumSettings::OpenImageExt(const QString& s) { setValue(OPENIMAGEEXT, s); }
QString FractoriumSettings::SaveImageExt() { return value(SAVEIMAGEEXT).toString(); }
void FractoriumSettings::SaveImageExt(const QString& s) { setValue(SAVEIMAGEEXT, s); }
bool FractoriumSettings::SaveAutoUnique() { return value(AUTOUNIQUE).toBool(); }
void FractoriumSettings::SaveAutoUnique(bool b) { setValue(AUTOUNIQUE, b); }
QMap<QString, QVariant> FractoriumSettings::Variations() { return value(UIVARIATIONS).toMap(); }
void FractoriumSettings::Variations(const QMap<QString, QVariant>& m) { setValue(UIVARIATIONS, m); }
QString FractoriumSettings::Theme() { return value(STYLETHEME).toString(); }
void FractoriumSettings::Theme(const QString& s) { setValue(STYLETHEME, s); }
#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>
FractoriumSettings::FractoriumSettings()
#ifdef _WIN32
: QSettings(QSettings::IniFormat, QSettings::UserScope, "Fractorium", "Fractorium", nullptr)
#else
: QSettings(QSettings::IniFormat, QSettings::UserScope, "fractorium", "fractorium", nullptr)
#endif
{
EnsureDefaults();
}
/// <summary>
/// Make sure options have reasonable values in them first.
/// </summary>
void FractoriumSettings::EnsureDefaults()
{
auto info = OpenCLInfo::Instance();
if (FinalQuality() == 0)
FinalQuality(1000);
if (FinalTemporalSamples() == 0)
FinalTemporalSamples(100);
if (FinalSupersample() == 0)
FinalSupersample(2);
if (FinalStrips() == 0)
FinalStrips(1);
if (XmlTemporalSamples() == 0)
XmlTemporalSamples(100);
if (XmlQuality() == 0)
XmlQuality(1000);
if (XmlSupersample() == 0)
XmlSupersample(2);
if (Devices().empty() && !info->Devices().empty())
Devices(QList<QVariant> { 0 });
if (ThreadCount() == 0 || ThreadCount() > Timing::ProcessorCount())
ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Default to one less to keep the UI responsive for first time users.
if (FinalThreadCount() == 0 || FinalThreadCount() > Timing::ProcessorCount())
FinalThreadCount(Timing::ProcessorCount());
FinalThreadPriority(Clamp<int>(FinalThreadPriority(), static_cast<int>(eThreadPriority::LOWEST), static_cast<int>(eThreadPriority::HIGHEST)));
CpuSubBatch(std::max(1u, CpuSubBatch()));
OpenCLSubBatch(std::max(1u, OpenCLSubBatch()));
if (OpenCLSubBatchPct() == 0)
OpenCLSubBatchPct(0.025);//Default to 256 iters per thread when using the default sub batch size of 10,240.
OpenCLSubBatchPct(Clamp(OpenCLSubBatchPct(), 0.01, 1.0));
if (FinalOpenCLSubBatchPct() == 0)
FinalOpenCLSubBatchPct(0.025);//Default to 256 iters per thread when using the default sub batch size of 10,240.
FinalOpenCLSubBatchPct(Clamp(FinalOpenCLSubBatchPct(), 0.01, 1.0));
RandomCount(std::max(1u, RandomCount()));
if (CpuQuality() == 0)
CpuQuality(10);
if (OpenClQuality() == 0)
OpenClQuality(30);
if (FinalScale() > static_cast<int>(eScaleType::SCALE_HEIGHT))
FinalScale(0);
if (OpenXmlExt() == "")
OpenXmlExt("*.flame");
if (SaveXmlExt() == "")
SaveXmlExt(".flame");
if (OpenImageExt() == "")
OpenImageExt("*.png");
if (SaveImageExt() == "")
SaveImageExt(".png");
if (FinalExt() != "jpg" && FinalExt() != "png" && FinalExt() != "exr"
#ifdef _WIN32
&& FinalExt() != "bmp"
#endif
)
FinalExt("png");
if (AnimationFps() == 0)
AnimationFps(30);
auto s = OpenFolder();
QDir dir(s);
if (s.isEmpty() || !dir.exists())
{
QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DesktopLocation);
if (!paths.empty())
OpenFolder(paths[0]);
}
s = SaveFolder();
dir = QDir(s);
if (s.isEmpty() || !dir.exists())
{
QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DesktopLocation);
if (!paths.empty())
SaveFolder(paths[0]);
}
if (OpenPaletteImageFolder() == "")
OpenPaletteImageFolder(QCoreApplication::applicationDirPath());
if (value(SHAREDTEXTURE).toString() == "")//Set this to true if the setting is missing because it only needs to be false for the rare system that has problems with shared textures.
SharedTexture(true);
Compat::m_Compat = Flam3Compat();
}
/// <summary>
/// Interactive renderer settings.
/// </summary>
bool FractoriumSettings::EarlyClip() { return value(EARLYCLIP).toBool(); }
void FractoriumSettings::EarlyClip(bool b) { setValue(EARLYCLIP, b); }
bool FractoriumSettings::YAxisUp() { return value(YAXISUP).toBool(); }
void FractoriumSettings::YAxisUp(bool b) { setValue(YAXISUP, b); }
bool FractoriumSettings::Transparency() { return value(TRANSPARENCY).toBool(); }
void FractoriumSettings::Transparency(bool b) { setValue(TRANSPARENCY, b); }
bool FractoriumSettings::OpenCL() { return value(OPENCL).toBool(); }
void FractoriumSettings::OpenCL(bool b) { setValue(OPENCL, b); }
bool FractoriumSettings::SharedTexture() { return value(SHAREDTEXTURE).toBool(); }
void FractoriumSettings::SharedTexture(bool b) { setValue(SHAREDTEXTURE, b); }
bool FractoriumSettings::Double() { return value(DOUBLEPRECISION).toBool(); }
void FractoriumSettings::Double(bool b) { setValue(DOUBLEPRECISION, b); }
bool FractoriumSettings::ShowAllXforms() { return value(SHOWALLXFORMS).toBool(); }
void FractoriumSettings::ShowAllXforms(bool b) { setValue(SHOWALLXFORMS, b); }
bool FractoriumSettings::ShowXforms() { return value(SHOWXFORMS, QVariant::fromValue(true)).toBool(); }
void FractoriumSettings::ShowXforms(bool b) { setValue(SHOWXFORMS, b); }
bool FractoriumSettings::ShowGrid() { return value(SHOWGRID, QVariant::fromValue(true)).toBool(); }
void FractoriumSettings::ShowGrid(bool b) { setValue(SHOWGRID, b); }
bool FractoriumSettings::ToggleType() { return value(TOGGLETYPE).toBool(); }
void FractoriumSettings::ToggleType(bool b) { setValue(TOGGLETYPE, b); }
bool FractoriumSettings::Png16Bit() { return value(PNG16BIT).toBool(); }
void FractoriumSettings::Png16Bit(bool b) { setValue(PNG16BIT, b); }
bool FractoriumSettings::ContinuousUpdate() { return value(CONTUPDATE).toBool(); }
void FractoriumSettings::ContinuousUpdate(bool b) { setValue(CONTUPDATE, b); }
QList<QVariant> FractoriumSettings::Devices() { return value(DEVICES).toList(); }
void FractoriumSettings::Devices(const QList<QVariant>& devices) { setValue(DEVICES, devices); }
uint FractoriumSettings::ThreadCount() { return value(THREADCOUNT).toUInt(); }
void FractoriumSettings::ThreadCount(uint 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); }
uint FractoriumSettings::CpuSubBatch() { return value(CPUSUBBATCH).toUInt(); }
void FractoriumSettings::CpuSubBatch(uint i) { setValue(CPUSUBBATCH, i); }
double FractoriumSettings::OpenCLSubBatchPct() { return value(OPENCLSUBBATCHPCT).toDouble(); }
void FractoriumSettings::OpenCLSubBatchPct(double d) { setValue(OPENCLSUBBATCHPCT, d); }
uint FractoriumSettings::OpenCLSubBatch() { return value(OPENCLSUBBATCH).toUInt(); }
void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH, i); }
uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); }
void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); }
uint FractoriumSettings::CpuQuality() { return value(CPUQUALITY).toUInt(); }
void FractoriumSettings::CpuQuality(uint i) { setValue(CPUQUALITY, i); }
uint FractoriumSettings::OpenClQuality() { return value(OPENCLQUALITY).toUInt(); }
void FractoriumSettings::OpenClQuality(uint i) { setValue(OPENCLQUALITY, i); }
bool FractoriumSettings::LoadLast() { return value(LOADLAST).toBool(); }
void FractoriumSettings::LoadLast(bool b) { setValue(LOADLAST, b); }
bool FractoriumSettings::RotateAndScale() { return value(ROTSCALE).toBool(); }
void FractoriumSettings::RotateAndScale(bool b) { setValue(ROTSCALE, b); }
bool FractoriumSettings::Flam3Compat() { return value(FLAM3COMPAT, true).toBool(); }
void FractoriumSettings::Flam3Compat(bool b) { setValue(FLAM3COMPAT, b); }
/// <summary>
/// Sequence generation settings.
/// </summary>
double FractoriumSettings::Stagger() { return value(STAGGER).toDouble(); }
void FractoriumSettings::Stagger(double d) { setValue(STAGGER, d); }
double FractoriumSettings::StaggerMax() { return value(STAGGERMAX).toDouble(); }
void FractoriumSettings::StaggerMax(double d) { setValue(STAGGERMAX, d); }
uint FractoriumSettings::FramesPerRot() { return value(FRAMESPERROT).toUInt(); }
void FractoriumSettings::FramesPerRot(uint i) { setValue(FRAMESPERROT, i); }
uint FractoriumSettings::FramesPerRotMax() { return value(FRAMESPERROTMAX).toUInt(); }
void FractoriumSettings::FramesPerRotMax(uint i) { setValue(FRAMESPERROTMAX, i); }
uint FractoriumSettings::Rotations() { return value(ROTATIONS).toDouble(); }
void FractoriumSettings::Rotations(double d) { setValue(ROTATIONS, d); }
uint FractoriumSettings::RotationsMax() { return value(ROTATIONSMAX).toDouble(); }
void FractoriumSettings::RotationsMax(double d) { setValue(ROTATIONSMAX, d); }
uint FractoriumSettings::BlendFrames() { return value(BLENDFRAMES).toUInt(); }
void FractoriumSettings::BlendFrames(uint i) { setValue(BLENDFRAMES, i); }
uint FractoriumSettings::BlendFramesMax() { return value(BLENDFRAMESMAX).toUInt(); }
void FractoriumSettings::BlendFramesMax(uint i) { setValue(BLENDFRAMESMAX, i); }
uint FractoriumSettings::RotationsPerBlend() { return value(ROTATIONSPERBLEND).toUInt(); }
void FractoriumSettings::RotationsPerBlend(uint i) { setValue(ROTATIONSPERBLEND, i); }
uint FractoriumSettings::RotationsPerBlendMax() { return value(ROTATIONSPERBLENDMAX).toUInt(); }
void FractoriumSettings::RotationsPerBlendMax(uint i) { setValue(ROTATIONSPERBLENDMAX, i); }
bool FractoriumSettings::Linear() { return value(LINEAR).toBool(); }
void FractoriumSettings::Linear(bool b) { setValue(LINEAR, b); }
uint FractoriumSettings::AnimationFps() { return value(ANIMATION_FPS).toUInt(); }
void FractoriumSettings::AnimationFps(uint i) { setValue(ANIMATION_FPS, i); }
/// <summary>
/// Variations filter settings.
/// </summary>
int FractoriumSettings::VarFilterSum() { return value(VARFILTERSUM).toInt(); }
void FractoriumSettings::VarFilterSum(int i) { setValue(VARFILTERSUM, i); }
int FractoriumSettings::VarFilterAssign() { return value(VARFILTERASSIGN).toInt(); }
void FractoriumSettings::VarFilterAssign(int i) { setValue(VARFILTERASSIGN, i); }
int FractoriumSettings::VarFilterPpsum() { return value(VARFILTERPPSUM).toInt(); }
void FractoriumSettings::VarFilterPpsum(int i) { setValue(VARFILTERPPSUM, i); }
int FractoriumSettings::VarFilterPpassign() { return value(VARFILTERPPASSIGN).toInt(); }
void FractoriumSettings::VarFilterPpassign(int i) { setValue(VARFILTERPPASSIGN, i); }
int FractoriumSettings::VarFilterSdc() { return value(VARFILTERSDC).toInt(); }
void FractoriumSettings::VarFilterSdc(int i) { setValue(VARFILTERSDC, i); }
int FractoriumSettings::VarFilterState() { return value(VARFILTERSTATE).toInt(); }
void FractoriumSettings::VarFilterState(int i) { setValue(VARFILTERSTATE, i); }
int FractoriumSettings::VarFilterParam() { return value(VARFILTERPARAM).toInt(); }
void FractoriumSettings::VarFilterParam(int i) { setValue(VARFILTERPARAM, i); }
int FractoriumSettings::VarFilterNonparam() { return value(VARFILTERNONPARAM).toInt(); }
void FractoriumSettings::VarFilterNonparam(int i) { setValue(VARFILTERNONPARAM, i); }
/// <summary>
/// Final render settings.
/// </summary>
bool FractoriumSettings::FinalEarlyClip() { return value(FINALEARLYCLIP).toBool(); }
void FractoriumSettings::FinalEarlyClip(bool b) { setValue(FINALEARLYCLIP, b); }
bool FractoriumSettings::FinalYAxisUp() { return value(FINALYAXISUP).toBool(); }
void FractoriumSettings::FinalYAxisUp(bool b) { setValue(FINALYAXISUP, 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::FinalPng16Bit() { return value(FINALPNG16BIT).toBool(); }
void FractoriumSettings::FinalPng16Bit(bool b) { setValue(FINALPNG16BIT, b); }
bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); }
void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); }
uint FractoriumSettings::FinalScale() { return value(FINALSCALE).toUInt(); }
void FractoriumSettings::FinalScale(uint i) { setValue(FINALSCALE, i); }
QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); }
void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); }
QList<QVariant> FractoriumSettings::FinalDevices() { return value(FINALDEVICES).toList(); }
void FractoriumSettings::FinalDevices(const QList<QVariant>& devices) { setValue(FINALDEVICES, devices); }
uint FractoriumSettings::FinalThreadCount() { return value(FINALTHREADCOUNT).toUInt(); }
void FractoriumSettings::FinalThreadCount(uint i) { setValue(FINALTHREADCOUNT, i); }
int FractoriumSettings::FinalThreadPriority() { return value(FINALTHREADPRIORITY).toInt(); }
void FractoriumSettings::FinalThreadPriority(int i) { setValue(FINALTHREADPRIORITY, i); }
double FractoriumSettings::FinalOpenCLSubBatchPct() { return value(FINALOPENCLSUBBATCHPCT).toDouble(); }
void FractoriumSettings::FinalOpenCLSubBatchPct(double d) { setValue(FINALOPENCLSUBBATCHPCT, d); }
uint FractoriumSettings::FinalQuality() { return value(FINALQUALITY).toUInt(); }
void FractoriumSettings::FinalQuality(uint i) { setValue(FINALQUALITY, i); }
uint FractoriumSettings::FinalTemporalSamples() { return value(FINALTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::FinalTemporalSamples(uint i) { setValue(FINALTEMPORALSAMPLES, i); }
uint FractoriumSettings::FinalSupersample() { return value(FINALSUPERSAMPLE).toUInt(); }
void FractoriumSettings::FinalSupersample(uint i) { setValue(FINALSUPERSAMPLE, i); }
size_t FractoriumSettings::FinalStrips() { return value(FINALSTRIPS).toULongLong(); }
void FractoriumSettings::FinalStrips(size_t i) { setValue(FINALSTRIPS, static_cast<uint>(i)); }
/// <summary>
/// Xml file saving settings.
/// </summary>
uint FractoriumSettings::XmlTemporalSamples() { return value(XMLTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::XmlTemporalSamples(uint i) { setValue(XMLTEMPORALSAMPLES, i); }
uint FractoriumSettings::XmlQuality() { return value(XMLQUALITY).toUInt(); }
void FractoriumSettings::XmlQuality(uint i) { setValue(XMLQUALITY, i); }
uint FractoriumSettings::XmlSupersample() { return value(XMLSUPERSAMPLE).toUInt(); }
void FractoriumSettings::XmlSupersample(uint i) { setValue(XMLSUPERSAMPLE, i); }
QString FractoriumSettings::Id() { return value(IDENTITYID).toString(); }
void FractoriumSettings::Id(const QString& s) { setValue(IDENTITYID, s); }
QString FractoriumSettings::Url() { return value(IDENTITYURL).toString(); }
void FractoriumSettings::Url(const QString& s) { setValue(IDENTITYURL, s); }
QString FractoriumSettings::Nick() { return value(IDENTITYNICK).toString(); }
void FractoriumSettings::Nick(const QString& s) { setValue(IDENTITYNICK, s); }
/// <summary>
/// General operations settings.
/// </summary>
QString FractoriumSettings::OpenFolder() { return value(OPENFOLDER).toString(); }
void FractoriumSettings::OpenFolder(const QString& s) { setValue(OPENFOLDER, s); }
QString FractoriumSettings::OpenPaletteImageFolder() { return value(OPENPALETTEIMAGEFOLDER).toString(); }
void FractoriumSettings::OpenPaletteImageFolder(const QString& s) { setValue(OPENPALETTEIMAGEFOLDER, s); }
QString FractoriumSettings::SaveFolder() { return value(SAVEFOLDER).toString(); }
void FractoriumSettings::SaveFolder(const QString& s) { setValue(SAVEFOLDER, s); }
QString FractoriumSettings::OpenXmlExt() { return value(OPENXMLEXT).toString(); }
void FractoriumSettings::OpenXmlExt(const QString& s) { setValue(OPENXMLEXT, s); }
QString FractoriumSettings::SaveXmlExt() { return value(SAVEXMLEXT).toString(); }
void FractoriumSettings::SaveXmlExt(const QString& s) { setValue(SAVEXMLEXT, s); }
QString FractoriumSettings::OpenImageExt() { return value(OPENIMAGEEXT).toString(); }
void FractoriumSettings::OpenImageExt(const QString& s) { setValue(OPENIMAGEEXT, s); }
QString FractoriumSettings::SaveImageExt() { return value(SAVEIMAGEEXT).toString(); }
void FractoriumSettings::SaveImageExt(const QString& s) { setValue(SAVEIMAGEEXT, s); }
bool FractoriumSettings::SaveAutoUnique() { return value(AUTOUNIQUE).toBool(); }
void FractoriumSettings::SaveAutoUnique(bool b) { setValue(AUTOUNIQUE, b); }
QMap<QString, QVariant> FractoriumSettings::Variations() { return value(UIVARIATIONS).toMap(); }
void FractoriumSettings::Variations(const QMap<QString, QVariant>& m) { setValue(UIVARIATIONS, m); }
QString FractoriumSettings::Theme() { return value(STYLETHEME).toString(); }
void FractoriumSettings::Theme(const QString& s) { setValue(STYLETHEME, s); }

View File

@ -1,359 +1,359 @@
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// FractoriumSettings class.
/// </summary>
#define EARLYCLIP "render/earlyclip"
#define YAXISUP "render/yaxisup"
#define TRANSPARENCY "render/transparency"
#define OPENCL "render/opencl"
#define SHAREDTEXTURE "render/sharedtexture"
#define DOUBLEPRECISION "render/dp64"
#define CONTUPDATE "render/continuousupdate"
#define SHOWALLXFORMS "render/dragshowallxforms"
#define SHOWXFORMS "render/showxforms"
#define SHOWGRID "render/showgrid"
#define TOGGLETYPE "render/toggletype"
#define PNG16BIT "render/png16bit"
#define DEVICES "render/devices"
#define THREADCOUNT "render/threadcount"
#define CPUDEFILTER "render/cpudefilter"
#define OPENCLDEFILTER "render/opencldefilter"
#define CPUSUBBATCH "render/cpusubbatch"
#define OPENCLSUBBATCH "render/openclsubbatch"
#define OPENCLSUBBATCHPCT "render/openclsubbatchpct"
#define RANDOMCOUNT "render/randomcount"
#define CPUQUALITY "render/cpuquality"
#define OPENCLQUALITY "render/openclquality"
#define LOADLAST "render/loadlastonstart"
#define ROTSCALE "render/rotateandscale"
#define FLAM3COMPAT "render/flam3compat"
#define STAGGER "sequence/stagger"
#define STAGGERMAX "sequence/staggermax"
#define FRAMESPERROT "sequence/framesperrot"
#define FRAMESPERROTMAX "sequence/framesperrotmax"
#define ROTATIONS "sequence/rotations"
#define ROTATIONSMAX "sequence/rotationsmax"
#define BLENDFRAMES "sequence/blendframes"
#define BLENDFRAMESMAX "sequence/blendframesmax"
#define ROTATIONSPERBLEND "sequence/rotationsperblend"
#define ROTATIONSPERBLENDMAX "sequence/rotationsperblendmax"
#define LINEAR "sequence/linear"
#define ANIMATION_FPS "sequence/animationfps"
#define VARFILTERSUM "varfilter/sumcheckbox"
#define VARFILTERASSIGN "varfilter/assigncheckbox"
#define VARFILTERPPSUM "varfilter/ppsumcheckbox"
#define VARFILTERPPASSIGN "varfilter/ppassigncheckbox"
#define VARFILTERSDC "varfilter/dccheckbox"
#define VARFILTERSTATE "varfilter/statecheckbox"
#define VARFILTERPARAM "varfilter/paramcheckbox"
#define VARFILTERNONPARAM "varfilter/nonparamcheckbox"
#define FINALEARLYCLIP "finalrender/earlyclip"
#define FINALYAXISUP "finalrender/finalyaxisup"
#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 FINALPNG16BIT "finalrender/png16bit"
#define FINALKEEPASPECT "finalrender/keepaspect"
#define FINALSCALE "finalrender/scale"
#define FINALEXT "finalrender/ext"
#define FINALDEVICES "finalrender/devices"
#define FINALTHREADCOUNT "finalrender/threadcount"
#define FINALTHREADPRIORITY "finalrender/threadpriority"
#define FINALOPENCLSUBBATCHPCT "finalrender/openclsubbatchpct"
#define FINALQUALITY "finalrender/quality"
#define FINALTEMPORALSAMPLES "finalrender/temporalsamples"
#define FINALSUPERSAMPLE "finalrender/supersample"
#define FINALSTRIPS "finalrender/strips"
#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 OPENPALETTEIMAGEFOLDER "path/openpaletteimage"
#define SAVEFOLDER "path/save"
#define OPENXMLEXT "file/openxmlext"
#define SAVEXMLEXT "file/savexmlext"
#define OPENIMAGEEXT "file/openimageext"
#define SAVEIMAGEEXT "file/saveimageext"
#define AUTOUNIQUE "file/autounique"
#define IDENTITYID "identity/id"
#define IDENTITYURL "identity/url"
#define IDENTITYNICK "identity/nick"
#define UIVARIATIONS "ui/variations"
#define STYLETHEME "style/theme"
/// <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, public Singleton<FractoriumSettings>
{
Q_OBJECT
public:
SINGLETON_DERIVED_IMPL(FractoriumSettings);
void EnsureDefaults();
bool EarlyClip();
void EarlyClip(bool b);
bool YAxisUp();
void YAxisUp(bool b);
bool Transparency();
void Transparency(bool b);
bool OpenCL();
void OpenCL(bool b);
bool SharedTexture();
void SharedTexture(bool b);
bool Double();
void Double(bool b);
bool ShowAllXforms();
void ShowAllXforms(bool b);
bool ShowXforms();
void ShowXforms(bool b);
bool ShowGrid();
void ShowGrid(bool b);
bool ToggleType();
void ToggleType(bool b);
bool Png16Bit();
void Png16Bit(bool b);
bool ContinuousUpdate();
void ContinuousUpdate(bool b);
QList<QVariant> Devices();
void Devices(const QList<QVariant>& devices);
uint ThreadCount();
void ThreadCount(uint i);
bool CpuDEFilter();
void CpuDEFilter(bool b);
bool OpenCLDEFilter();
void OpenCLDEFilter(bool b);
uint CpuSubBatch();
void CpuSubBatch(uint i);
double OpenCLSubBatchPct();
void OpenCLSubBatchPct(double d);
uint OpenCLSubBatch();
void OpenCLSubBatch(uint i);
uint RandomCount();
void RandomCount(uint i);
uint CpuQuality();
void CpuQuality(uint i);
uint OpenClQuality();
void OpenClQuality(uint i);
bool LoadLast();
void LoadLast(bool b);
bool RotateAndScale();
void RotateAndScale(bool b);
bool Flam3Compat();
void Flam3Compat(bool b);
double Stagger();
void Stagger(double i);
double StaggerMax();
void StaggerMax(double i);
uint FramesPerRot();
void FramesPerRot(uint i);
uint FramesPerRotMax();
void FramesPerRotMax(uint i);
uint Rotations();
void Rotations(double d);
uint RotationsMax();
void RotationsMax(double d);
uint BlendFrames();
void BlendFrames(uint i);
uint BlendFramesMax();
void BlendFramesMax(uint i);
uint RotationsPerBlend();
void RotationsPerBlend(uint i);
uint RotationsPerBlendMax();
void RotationsPerBlendMax(uint i);
bool Linear();
void Linear(bool b);
uint AnimationFps();
void AnimationFps(uint i);
int VarFilterSum();
void VarFilterSum(int i);
int VarFilterAssign();
void VarFilterAssign(int i);
int VarFilterPpsum();
void VarFilterPpsum(int i);
int VarFilterPpassign();
void VarFilterPpassign(int i);
int VarFilterSdc();
void VarFilterSdc(int i);
int VarFilterState();
void VarFilterState(int i);
int VarFilterParam();
void VarFilterParam(int i);
int VarFilterNonparam();
void VarFilterNonparam(int i);
bool FinalEarlyClip();
void FinalEarlyClip(bool b);
bool FinalYAxisUp();
void FinalYAxisUp(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 FinalPng16Bit();
void FinalPng16Bit(bool b);
bool FinalKeepAspect();
void FinalKeepAspect(bool b);
uint FinalScale();
void FinalScale(uint i);
QString FinalExt();
void FinalExt(const QString& s);
QList<QVariant> FinalDevices();
void FinalDevices(const QList<QVariant>& devices);
uint FinalThreadCount();
void FinalThreadCount(uint i);
int FinalThreadPriority();
void FinalThreadPriority(int b);
double FinalOpenCLSubBatchPct();
void FinalOpenCLSubBatchPct(double d);
uint FinalQuality();
void FinalQuality(uint i);
uint FinalTemporalSamples();
void FinalTemporalSamples(uint i);
uint FinalSupersample();
void FinalSupersample(uint i);
size_t FinalStrips();
void FinalStrips(size_t i);
uint XmlTemporalSamples();
void XmlTemporalSamples(uint i);
uint XmlQuality();
void XmlQuality(uint i);
uint XmlSupersample();
void XmlSupersample(uint i);
QString OpenFolder();
void OpenFolder(const QString& s);
QString OpenPaletteImageFolder();
void OpenPaletteImageFolder(const QString& s);
QString SaveFolder();
void SaveFolder(const QString& s);
QString OpenXmlExt();
void OpenXmlExt(const QString& s);
QString SaveXmlExt();
void SaveXmlExt(const QString& s);
QString OpenImageExt();
void OpenImageExt(const QString& s);
QString SaveImageExt();
void SaveImageExt(const QString& s);
bool SaveAutoUnique();
void SaveAutoUnique(bool b);
QString Id();
void Id(const QString& s);
QString Url();
void Url(const QString& s);
QString Nick();
void Nick(const QString& s);
QMap<QString, QVariant> Variations();
void Variations(const QMap<QString, QVariant>& m);
QString Theme();
void Theme(const QString& s);
private:
FractoriumSettings();
};
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// FractoriumSettings class.
/// </summary>
#define EARLYCLIP "render/earlyclip"
#define YAXISUP "render/yaxisup"
#define TRANSPARENCY "render/transparency"
#define OPENCL "render/opencl"
#define SHAREDTEXTURE "render/sharedtexture"
#define DOUBLEPRECISION "render/dp64"
#define CONTUPDATE "render/continuousupdate"
#define SHOWALLXFORMS "render/dragshowallxforms"
#define SHOWXFORMS "render/showxforms"
#define SHOWGRID "render/showgrid"
#define TOGGLETYPE "render/toggletype"
#define PNG16BIT "render/png16bit"
#define DEVICES "render/devices"
#define THREADCOUNT "render/threadcount"
#define CPUDEFILTER "render/cpudefilter"
#define OPENCLDEFILTER "render/opencldefilter"
#define CPUSUBBATCH "render/cpusubbatch"
#define OPENCLSUBBATCH "render/openclsubbatch"
#define OPENCLSUBBATCHPCT "render/openclsubbatchpct"
#define RANDOMCOUNT "render/randomcount"
#define CPUQUALITY "render/cpuquality"
#define OPENCLQUALITY "render/openclquality"
#define LOADLAST "render/loadlastonstart"
#define ROTSCALE "render/rotateandscale"
#define FLAM3COMPAT "render/flam3compat"
#define STAGGER "sequence/stagger"
#define STAGGERMAX "sequence/staggermax"
#define FRAMESPERROT "sequence/framesperrot"
#define FRAMESPERROTMAX "sequence/framesperrotmax"
#define ROTATIONS "sequence/rotations"
#define ROTATIONSMAX "sequence/rotationsmax"
#define BLENDFRAMES "sequence/blendframes"
#define BLENDFRAMESMAX "sequence/blendframesmax"
#define ROTATIONSPERBLEND "sequence/rotationsperblend"
#define ROTATIONSPERBLENDMAX "sequence/rotationsperblendmax"
#define LINEAR "sequence/linear"
#define ANIMATION_FPS "sequence/animationfps"
#define VARFILTERSUM "varfilter/sumcheckbox"
#define VARFILTERASSIGN "varfilter/assigncheckbox"
#define VARFILTERPPSUM "varfilter/ppsumcheckbox"
#define VARFILTERPPASSIGN "varfilter/ppassigncheckbox"
#define VARFILTERSDC "varfilter/dccheckbox"
#define VARFILTERSTATE "varfilter/statecheckbox"
#define VARFILTERPARAM "varfilter/paramcheckbox"
#define VARFILTERNONPARAM "varfilter/nonparamcheckbox"
#define FINALEARLYCLIP "finalrender/earlyclip"
#define FINALYAXISUP "finalrender/finalyaxisup"
#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 FINALPNG16BIT "finalrender/png16bit"
#define FINALKEEPASPECT "finalrender/keepaspect"
#define FINALSCALE "finalrender/scale"
#define FINALEXT "finalrender/ext"
#define FINALDEVICES "finalrender/devices"
#define FINALTHREADCOUNT "finalrender/threadcount"
#define FINALTHREADPRIORITY "finalrender/threadpriority"
#define FINALOPENCLSUBBATCHPCT "finalrender/openclsubbatchpct"
#define FINALQUALITY "finalrender/quality"
#define FINALTEMPORALSAMPLES "finalrender/temporalsamples"
#define FINALSUPERSAMPLE "finalrender/supersample"
#define FINALSTRIPS "finalrender/strips"
#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 OPENPALETTEIMAGEFOLDER "path/openpaletteimage"
#define SAVEFOLDER "path/save"
#define OPENXMLEXT "file/openxmlext"
#define SAVEXMLEXT "file/savexmlext"
#define OPENIMAGEEXT "file/openimageext"
#define SAVEIMAGEEXT "file/saveimageext"
#define AUTOUNIQUE "file/autounique"
#define IDENTITYID "identity/id"
#define IDENTITYURL "identity/url"
#define IDENTITYNICK "identity/nick"
#define UIVARIATIONS "ui/variations"
#define STYLETHEME "style/theme"
/// <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, public Singleton<FractoriumSettings>
{
Q_OBJECT
public:
SINGLETON_DERIVED_IMPL(FractoriumSettings);
void EnsureDefaults();
bool EarlyClip();
void EarlyClip(bool b);
bool YAxisUp();
void YAxisUp(bool b);
bool Transparency();
void Transparency(bool b);
bool OpenCL();
void OpenCL(bool b);
bool SharedTexture();
void SharedTexture(bool b);
bool Double();
void Double(bool b);
bool ShowAllXforms();
void ShowAllXforms(bool b);
bool ShowXforms();
void ShowXforms(bool b);
bool ShowGrid();
void ShowGrid(bool b);
bool ToggleType();
void ToggleType(bool b);
bool Png16Bit();
void Png16Bit(bool b);
bool ContinuousUpdate();
void ContinuousUpdate(bool b);
QList<QVariant> Devices();
void Devices(const QList<QVariant>& devices);
uint ThreadCount();
void ThreadCount(uint i);
bool CpuDEFilter();
void CpuDEFilter(bool b);
bool OpenCLDEFilter();
void OpenCLDEFilter(bool b);
uint CpuSubBatch();
void CpuSubBatch(uint i);
double OpenCLSubBatchPct();
void OpenCLSubBatchPct(double d);
uint OpenCLSubBatch();
void OpenCLSubBatch(uint i);
uint RandomCount();
void RandomCount(uint i);
uint CpuQuality();
void CpuQuality(uint i);
uint OpenClQuality();
void OpenClQuality(uint i);
bool LoadLast();
void LoadLast(bool b);
bool RotateAndScale();
void RotateAndScale(bool b);
bool Flam3Compat();
void Flam3Compat(bool b);
double Stagger();
void Stagger(double i);
double StaggerMax();
void StaggerMax(double i);
uint FramesPerRot();
void FramesPerRot(uint i);
uint FramesPerRotMax();
void FramesPerRotMax(uint i);
uint Rotations();
void Rotations(double d);
uint RotationsMax();
void RotationsMax(double d);
uint BlendFrames();
void BlendFrames(uint i);
uint BlendFramesMax();
void BlendFramesMax(uint i);
uint RotationsPerBlend();
void RotationsPerBlend(uint i);
uint RotationsPerBlendMax();
void RotationsPerBlendMax(uint i);
bool Linear();
void Linear(bool b);
uint AnimationFps();
void AnimationFps(uint i);
int VarFilterSum();
void VarFilterSum(int i);
int VarFilterAssign();
void VarFilterAssign(int i);
int VarFilterPpsum();
void VarFilterPpsum(int i);
int VarFilterPpassign();
void VarFilterPpassign(int i);
int VarFilterSdc();
void VarFilterSdc(int i);
int VarFilterState();
void VarFilterState(int i);
int VarFilterParam();
void VarFilterParam(int i);
int VarFilterNonparam();
void VarFilterNonparam(int i);
bool FinalEarlyClip();
void FinalEarlyClip(bool b);
bool FinalYAxisUp();
void FinalYAxisUp(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 FinalPng16Bit();
void FinalPng16Bit(bool b);
bool FinalKeepAspect();
void FinalKeepAspect(bool b);
uint FinalScale();
void FinalScale(uint i);
QString FinalExt();
void FinalExt(const QString& s);
QList<QVariant> FinalDevices();
void FinalDevices(const QList<QVariant>& devices);
uint FinalThreadCount();
void FinalThreadCount(uint i);
int FinalThreadPriority();
void FinalThreadPriority(int b);
double FinalOpenCLSubBatchPct();
void FinalOpenCLSubBatchPct(double d);
uint FinalQuality();
void FinalQuality(uint i);
uint FinalTemporalSamples();
void FinalTemporalSamples(uint i);
uint FinalSupersample();
void FinalSupersample(uint i);
size_t FinalStrips();
void FinalStrips(size_t i);
uint XmlTemporalSamples();
void XmlTemporalSamples(uint i);
uint XmlQuality();
void XmlQuality(uint i);
uint XmlSupersample();
void XmlSupersample(uint i);
QString OpenFolder();
void OpenFolder(const QString& s);
QString OpenPaletteImageFolder();
void OpenPaletteImageFolder(const QString& s);
QString SaveFolder();
void SaveFolder(const QString& s);
QString OpenXmlExt();
void OpenXmlExt(const QString& s);
QString SaveXmlExt();
void SaveXmlExt(const QString& s);
QString OpenImageExt();
void OpenImageExt(const QString& s);
QString SaveImageExt();
void SaveImageExt(const QString& s);
bool SaveAutoUnique();
void SaveAutoUnique(bool b);
QString Id();
void Id(const QString& s);
QString Url();
void Url(const QString& s);
QString Nick();
void Nick(const QString& s);
QMap<QString, QVariant> Variations();
void Variations(const QMap<QString, QVariant>& m);
QString Theme();
void Theme(const QString& s);
private:
FractoriumSettings();
};

View File

@ -1,252 +1,252 @@
#include "FractoriumPch.h"
#include "Fractorium.h"
#include "QssDialog.h"
/// <summary>
/// Initialize the toolbar UI.
/// </summary>
void Fractorium::InitToolbarUI()
{
auto clGroup = new QActionGroup(this);
clGroup->addAction(ui.ActionCpu);
clGroup->addAction(ui.ActionCL);
auto spGroup = new QActionGroup(this);
spGroup->addAction(ui.ActionSP);
spGroup->addAction(ui.ActionDP);
SyncOptionsToToolbar();
ui.ActionDrawImage->setChecked(true);
m_PreviousAffineState[int(eAffineState::PRE) ] = true;
m_PreviousAffineState[int(eAffineState::ALL_PRE) ] = true;
m_PreviousAffineState[int(eAffineState::POST) ] = false;
m_PreviousAffineState[int(eAffineState::ALL_POST)] = false;
connect(ui.ActionCpu, SIGNAL(triggered(bool)), this, SLOT(OnActionCpu(bool)), Qt::QueuedConnection);
connect(ui.ActionCL, SIGNAL(triggered(bool)), this, SLOT(OnActionCL(bool)), Qt::QueuedConnection);
connect(ui.ActionSP, SIGNAL(triggered(bool)), this, SLOT(OnActionSP(bool)), Qt::QueuedConnection);
connect(ui.ActionDP, SIGNAL(triggered(bool)), this, SLOT(OnActionDP(bool)), Qt::QueuedConnection);
connect(ui.ActionStyle, SIGNAL(triggered(bool)), this, SLOT(OnActionStyle(bool)), Qt::QueuedConnection);
connect(ui.ActionStartStopRenderer, SIGNAL(triggered(bool)), this, SLOT(OnActionStartStopRenderer(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawImage, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawImage(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawPreAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawPostAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawAllPreAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAllAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawAllPostAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAllAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawGrid, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawGrid(bool)), Qt::QueuedConnection);
}
/// <summary>
/// GUI wrapper functions, getters only.
/// </summary>
bool Fractorium::DrawPreAffines() { return ui.ActionDrawPreAffines->isChecked(); }
bool Fractorium::DrawPostAffines() { return ui.ActionDrawPostAffines->isChecked(); }
bool Fractorium::DrawSelectedPre() { return !ui.ActionDrawAllPreAffines->isChecked(); }
bool Fractorium::DrawAllPre() { return ui.ActionDrawAllPreAffines->isChecked(); }
bool Fractorium::DrawSelectedPost() { return !ui.ActionDrawAllPostAffines->isChecked(); }
bool Fractorium::DrawAllPost() { return ui.ActionDrawAllPostAffines->isChecked(); }
bool Fractorium::DrawXforms() { return ui.ActionDrawPreAffines->isChecked() || ui.ActionDrawPostAffines->isChecked(); }
bool Fractorium::DrawImage() { return ui.ActionDrawImage->isChecked(); }
bool Fractorium::DrawGrid() { return ui.ActionDrawGrid->isChecked(); }
/// <summary>
/// Called when the CPU render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionCpu(bool checked)
{
if (checked && m_Settings->OpenCL())
{
m_Settings->OpenCL(false);
ShutdownAndRecreateFromOptions(false);
}
}
/// <summary>
/// Called when the OpenCL render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionCL(bool checked)
{
if (checked && !m_Settings->OpenCL())
{
m_Settings->OpenCL(true);
ShutdownAndRecreateFromOptions(false);
}
}
/// <summary>
/// Called when the single precision render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionSP(bool checked)
{
if (checked && m_Settings->Double())
{
m_Settings->Double(false);
ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render.
}
}
/// <summary>
/// Called when the double precision render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionDP(bool checked)
{
if (checked && !m_Settings->Double())
{
m_Settings->Double(true);
ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render.
}
}
/// <summary>
/// Called when the show style button is clicked.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnActionStyle(bool checked)
{
#ifdef __APPLE__
m_QssDialog->exec();
#else
m_QssDialog->show();
#endif
}
/// <summary>
/// Called when the start/stop renderer button is clicked.
/// </summary>
/// <param name="checked">Check state, stop renderer if true, else start.</param>
void Fractorium::OnActionStartStopRenderer(bool checked)
{
EnableRenderControls(!checked);
if (checked)
{
m_Controller->StopRenderTimer(true);
ui.ActionStartStopRenderer->setToolTip("Start Renderer");
ui.ActionStartStopRenderer->setIcon(QIcon(":/Fractorium/Icons/control.png"));
}
else
{
m_Controller->StartRenderTimer();
ui.ActionStartStopRenderer->setToolTip("Stop Renderer");
ui.ActionStartStopRenderer->setIcon(QIcon(":/Fractorium/Icons/control-stop-square.png"));
}
}
/// <summary>
/// Called when the pre affine button is clicked.
/// </summary>
/// <param name="checked">Check state, show pre affines if true, else hide.</param>
void Fractorium::OnActionDrawAffines(bool checked)
{
m_Settings->ShowXforms(checked);
if (!ui.ActionDrawImage->isChecked() && !(ui.ActionDrawPreAffines->isChecked() || ui.ActionDrawPostAffines->isChecked()))
ui.ActionDrawImage->setChecked(true);
SaveAffineState();
ui.GLDisplay->update();
}
/// <summary>
/// Toggle whether to show selected/all post affines.
/// Called when the show all post affine button is clicked.
/// </summary>
/// <param name="checked">Check state, show all pre affines if true, else show selected.</param>
void Fractorium::OnActionDrawAllAffines(bool checked)
{
SaveAffineState();
ui.GLDisplay->update();
}
/// <summary>
/// Toggle whether to show the image.
/// Called when the image button is clicked.
/// </summary>
/// <param name="checked">Check state, show image if true, else hide.</param>
void Fractorium::OnActionDrawImage(bool checked)
{
if (!ui.ActionDrawImage->isChecked())
SyncAffineStateToToolbar();
ui.GLDisplay->update();
}
/// <summary>
/// Toggle whether to show the grid.
/// Called when the grid image button is clicked.
/// </summary>
/// <param name="checked">Check state, show grid if true, else hide.</param>
void Fractorium::OnActionDrawGrid(bool checked)
{
m_Settings->ShowGrid(checked);
ui.GLDisplay->update();
}
/// <summary>
/// Keep previous state of the affines to switch between Editor and Image
/// </summary>
void Fractorium::SaveAffineState()
{
if (!ui.ActionDrawPreAffines->isChecked() && !ui.ActionDrawPostAffines->isChecked())
return;
m_PreviousAffineState[int(eAffineState::PRE) ] = ui.ActionDrawPreAffines->isChecked();
m_PreviousAffineState[int(eAffineState::ALL_PRE) ] = ui.ActionDrawAllPreAffines->isChecked();
m_PreviousAffineState[int(eAffineState::POST) ] = ui.ActionDrawPostAffines->isChecked();
m_PreviousAffineState[int(eAffineState::ALL_POST)] = ui.ActionDrawAllPostAffines->isChecked();
}
/// <summary>
/// Sync affine state data to the check state of the toolbar buttons.
/// This does not trigger a clicked() event.
/// </summary>
void Fractorium::SyncAffineStateToToolbar()
{
ui.ActionDrawPreAffines->setChecked(m_PreviousAffineState [int(eAffineState::PRE) ]);
ui.ActionDrawAllPreAffines->setChecked(m_PreviousAffineState [int(eAffineState::ALL_PRE) ]);
ui.ActionDrawPostAffines->setChecked(m_PreviousAffineState [int(eAffineState::POST) ]);
ui.ActionDrawAllPostAffines->setChecked(m_PreviousAffineState[int(eAffineState::ALL_POST)]);
}
/// <summary>
/// Sync options data to the check state of the toolbar buttons.
/// This does not trigger a clicked() event.
/// </summary>
void Fractorium::SyncOptionsToToolbar()
{
static bool openCL = !m_Info->Devices().empty();
if (!openCL)
{
ui.ActionCL->setEnabled(false);
}
if (openCL && m_Settings->OpenCL())
{
ui.ActionCpu->setChecked(false);
ui.ActionCL->setChecked(true);
}
else
{
ui.ActionCpu->setChecked(true);
ui.ActionCL->setChecked(false);
}
if (m_Settings->Double())
{
ui.ActionSP->setChecked(false);
ui.ActionDP->setChecked(true);
}
else
{
ui.ActionSP->setChecked(true);
ui.ActionDP->setChecked(false);
}
ui.ActionDrawGrid->setChecked(m_Settings->ShowGrid());
ui.ActionDrawPreAffines->setChecked(m_Settings->ShowXforms());
ui.ActionDrawAllPreAffines->setChecked(m_Settings->ShowXforms());
SaveAffineState();
}
#include "FractoriumPch.h"
#include "Fractorium.h"
#include "QssDialog.h"
/// <summary>
/// Initialize the toolbar UI.
/// </summary>
void Fractorium::InitToolbarUI()
{
auto clGroup = new QActionGroup(this);
clGroup->addAction(ui.ActionCpu);
clGroup->addAction(ui.ActionCL);
auto spGroup = new QActionGroup(this);
spGroup->addAction(ui.ActionSP);
spGroup->addAction(ui.ActionDP);
SyncOptionsToToolbar();
ui.ActionDrawImage->setChecked(true);
m_PreviousAffineState[int(eAffineState::PRE) ] = true;
m_PreviousAffineState[int(eAffineState::ALL_PRE) ] = true;
m_PreviousAffineState[int(eAffineState::POST) ] = false;
m_PreviousAffineState[int(eAffineState::ALL_POST)] = false;
connect(ui.ActionCpu, SIGNAL(triggered(bool)), this, SLOT(OnActionCpu(bool)), Qt::QueuedConnection);
connect(ui.ActionCL, SIGNAL(triggered(bool)), this, SLOT(OnActionCL(bool)), Qt::QueuedConnection);
connect(ui.ActionSP, SIGNAL(triggered(bool)), this, SLOT(OnActionSP(bool)), Qt::QueuedConnection);
connect(ui.ActionDP, SIGNAL(triggered(bool)), this, SLOT(OnActionDP(bool)), Qt::QueuedConnection);
connect(ui.ActionStyle, SIGNAL(triggered(bool)), this, SLOT(OnActionStyle(bool)), Qt::QueuedConnection);
connect(ui.ActionStartStopRenderer, SIGNAL(triggered(bool)), this, SLOT(OnActionStartStopRenderer(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawImage, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawImage(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawPreAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawPostAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawAllPreAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAllAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawAllPostAffines, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawAllAffines(bool)), Qt::QueuedConnection);
connect(ui.ActionDrawGrid, SIGNAL(triggered(bool)), this, SLOT(OnActionDrawGrid(bool)), Qt::QueuedConnection);
}
/// <summary>
/// GUI wrapper functions, getters only.
/// </summary>
bool Fractorium::DrawPreAffines() { return ui.ActionDrawPreAffines->isChecked(); }
bool Fractorium::DrawPostAffines() { return ui.ActionDrawPostAffines->isChecked(); }
bool Fractorium::DrawSelectedPre() { return !ui.ActionDrawAllPreAffines->isChecked(); }
bool Fractorium::DrawAllPre() { return ui.ActionDrawAllPreAffines->isChecked(); }
bool Fractorium::DrawSelectedPost() { return !ui.ActionDrawAllPostAffines->isChecked(); }
bool Fractorium::DrawAllPost() { return ui.ActionDrawAllPostAffines->isChecked(); }
bool Fractorium::DrawXforms() { return ui.ActionDrawPreAffines->isChecked() || ui.ActionDrawPostAffines->isChecked(); }
bool Fractorium::DrawImage() { return ui.ActionDrawImage->isChecked(); }
bool Fractorium::DrawGrid() { return ui.ActionDrawGrid->isChecked(); }
/// <summary>
/// Called when the CPU render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionCpu(bool checked)
{
if (checked && m_Settings->OpenCL())
{
m_Settings->OpenCL(false);
ShutdownAndRecreateFromOptions(false);
}
}
/// <summary>
/// Called when the OpenCL render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionCL(bool checked)
{
if (checked && !m_Settings->OpenCL())
{
m_Settings->OpenCL(true);
ShutdownAndRecreateFromOptions(false);
}
}
/// <summary>
/// Called when the single precision render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionSP(bool checked)
{
if (checked && m_Settings->Double())
{
m_Settings->Double(false);
ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render.
}
}
/// <summary>
/// Called when the double precision render option on the toolbar is clicked.
/// </summary>
/// <param name="checked">Check state, action only taken if true.</param>
void Fractorium::OnActionDP(bool checked)
{
if (checked && !m_Settings->Double())
{
m_Settings->Double(true);
ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render.
}
}
/// <summary>
/// Called when the show style button is clicked.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnActionStyle(bool checked)
{
#ifdef __APPLE__
m_QssDialog->exec();
#else
m_QssDialog->show();
#endif
}
/// <summary>
/// Called when the start/stop renderer button is clicked.
/// </summary>
/// <param name="checked">Check state, stop renderer if true, else start.</param>
void Fractorium::OnActionStartStopRenderer(bool checked)
{
EnableRenderControls(!checked);
if (checked)
{
m_Controller->StopRenderTimer(true);
ui.ActionStartStopRenderer->setToolTip("Start Renderer");
ui.ActionStartStopRenderer->setIcon(QIcon(":/Fractorium/Icons/control.png"));
}
else
{
m_Controller->StartRenderTimer();
ui.ActionStartStopRenderer->setToolTip("Stop Renderer");
ui.ActionStartStopRenderer->setIcon(QIcon(":/Fractorium/Icons/control-stop-square.png"));
}
}
/// <summary>
/// Called when the pre affine button is clicked.
/// </summary>
/// <param name="checked">Check state, show pre affines if true, else hide.</param>
void Fractorium::OnActionDrawAffines(bool checked)
{
m_Settings->ShowXforms(checked);
if (!ui.ActionDrawImage->isChecked() && !(ui.ActionDrawPreAffines->isChecked() || ui.ActionDrawPostAffines->isChecked()))
ui.ActionDrawImage->setChecked(true);
SaveAffineState();
ui.GLDisplay->update();
}
/// <summary>
/// Toggle whether to show selected/all post affines.
/// Called when the show all post affine button is clicked.
/// </summary>
/// <param name="checked">Check state, show all pre affines if true, else show selected.</param>
void Fractorium::OnActionDrawAllAffines(bool checked)
{
SaveAffineState();
ui.GLDisplay->update();
}
/// <summary>
/// Toggle whether to show the image.
/// Called when the image button is clicked.
/// </summary>
/// <param name="checked">Check state, show image if true, else hide.</param>
void Fractorium::OnActionDrawImage(bool checked)
{
if (!ui.ActionDrawImage->isChecked())
SyncAffineStateToToolbar();
ui.GLDisplay->update();
}
/// <summary>
/// Toggle whether to show the grid.
/// Called when the grid image button is clicked.
/// </summary>
/// <param name="checked">Check state, show grid if true, else hide.</param>
void Fractorium::OnActionDrawGrid(bool checked)
{
m_Settings->ShowGrid(checked);
ui.GLDisplay->update();
}
/// <summary>
/// Keep previous state of the affines to switch between Editor and Image
/// </summary>
void Fractorium::SaveAffineState()
{
if (!ui.ActionDrawPreAffines->isChecked() && !ui.ActionDrawPostAffines->isChecked())
return;
m_PreviousAffineState[int(eAffineState::PRE) ] = ui.ActionDrawPreAffines->isChecked();
m_PreviousAffineState[int(eAffineState::ALL_PRE) ] = ui.ActionDrawAllPreAffines->isChecked();
m_PreviousAffineState[int(eAffineState::POST) ] = ui.ActionDrawPostAffines->isChecked();
m_PreviousAffineState[int(eAffineState::ALL_POST)] = ui.ActionDrawAllPostAffines->isChecked();
}
/// <summary>
/// Sync affine state data to the check state of the toolbar buttons.
/// This does not trigger a clicked() event.
/// </summary>
void Fractorium::SyncAffineStateToToolbar()
{
ui.ActionDrawPreAffines->setChecked(m_PreviousAffineState [int(eAffineState::PRE) ]);
ui.ActionDrawAllPreAffines->setChecked(m_PreviousAffineState [int(eAffineState::ALL_PRE) ]);
ui.ActionDrawPostAffines->setChecked(m_PreviousAffineState [int(eAffineState::POST) ]);
ui.ActionDrawAllPostAffines->setChecked(m_PreviousAffineState[int(eAffineState::ALL_POST)]);
}
/// <summary>
/// Sync options data to the check state of the toolbar buttons.
/// This does not trigger a clicked() event.
/// </summary>
void Fractorium::SyncOptionsToToolbar()
{
static bool openCL = !m_Info->Devices().empty();
if (!openCL)
{
ui.ActionCL->setEnabled(false);
}
if (openCL && m_Settings->OpenCL())
{
ui.ActionCpu->setChecked(false);
ui.ActionCL->setChecked(true);
}
else
{
ui.ActionCpu->setChecked(true);
ui.ActionCL->setChecked(false);
}
if (m_Settings->Double())
{
ui.ActionSP->setChecked(false);
ui.ActionDP->setChecked(true);
}
else
{
ui.ActionSP->setChecked(true);
ui.ActionDP->setChecked(false);
}
ui.ActionDrawGrid->setChecked(m_Settings->ShowGrid());
ui.ActionDrawPreAffines->setChecked(m_Settings->ShowXforms());
ui.ActionDrawAllPreAffines->setChecked(m_Settings->ShowXforms());
SaveAffineState();
}

View File

@ -1,398 +1,398 @@
#include "FractoriumPch.h"
#include "Fractorium.h"
#define XAOS_PREC 6
/// <summary>
/// Initialize the xforms xaos UI.
/// </summary>
void Fractorium::InitXaosUI()
{
int spinHeight = 20;
ui.XaosTableView->verticalHeader()->setSectionsClickable(true);
ui.XaosTableView->horizontalHeader()->setSectionsClickable(true);
m_XaosSpinBox = new DoubleSpinBox(nullptr, spinHeight, 0.1, false);
m_XaosSpinBox->DoubleClick(true);
m_XaosSpinBox->DoubleClickZero(1);
m_XaosSpinBox->DoubleClickNonZero(0);
m_XaosSpinBox->setDecimals(XAOS_PREC);
m_XaosSpinBox->setObjectName("XaosSpinBox");
m_XaosTableModel = nullptr;
m_AppliedXaosTableModel = nullptr;
m_XaosTableItemDelegate = new DoubleSpinBoxTableItemDelegate(m_XaosSpinBox, this);
connect(m_XaosSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnXaosChanged(double)), Qt::QueuedConnection);
connect(ui.ClearXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.RandomXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.TransposeXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnTransposeXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.AddLayerButton, SIGNAL(clicked(bool)), this, SLOT(OnAddLayerButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.XaosTableView->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosRowDoubleClicked(int)), Qt::QueuedConnection);
connect(ui.XaosTableView->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosColDoubleClicked(int)), Qt::QueuedConnection);
connect(ui.XaosTableView->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(OnXaosHScrollValueChanged(int)), Qt::QueuedConnection);
connect(ui.XaosTableView->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(OnXaosVScrollValueChanged(int)), Qt::QueuedConnection);
}
/// <summary>
/// Fill the xaos table with the values from the ember.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillXaos()
{
for (int i = 0, count = static_cast<int>(XformCount()); i < count; i++)//Column.
{
if (const auto xform = m_Ember.GetXform(i))
{
for (int j = 0; j < count; j++)//Row.
{
QModelIndex index = m_Fractorium->m_XaosTableModel->index(j, i, QModelIndex());//j and i are intentionally swapped here.
m_Fractorium->m_XaosTableModel->setData(index, xform->Xaos(j));
}
}
}
m_Fractorium->ui.XaosTableView->resizeRowsToContents();
m_Fractorium->ui.XaosTableView->resizeColumnsToContents();
}
/// <summary>
/// Fill the xaos table with the xaos values applied to the xform weights from the ember.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillAppliedXaos()
{
m_Ember.CalcNormalizedWeights(m_NormalizedWeights);
for (int i = 0, count = int(XformCount()); i < count; i++)//Column.
{
if (const auto xform = m_Ember.GetXform(i))
{
T norm = 0;
double start = 0, offset = 0;
auto tempweights = m_NormalizedWeights;
for (int j = 0; j < count; j++)//Row.
{
tempweights[j] *= xform->Xaos(j);
QModelIndex index = m_Fractorium->m_AppliedXaosTableModel->index(j, i, QModelIndex());//j and i are intentionally swapped here.
m_Fractorium->m_AppliedXaosTableModel->setData(index, TruncPrecision(xform->Xaos(j) * xform->m_Weight, 4));//Applied xaos is just a read only table for display purposes.
}
QPixmap pixmap(m_Fractorium->ui.XaosAppliedTableView->columnWidth(i) - 8, m_Fractorium->ui.XaosTableView->rowHeight(0) * count);
QPainter painter(&pixmap);
auto twi = std::make_unique<QTableWidgetItem>();
for (auto& w : tempweights) norm += w;
for (auto& w : tempweights) w = norm == static_cast<T>(0) ? static_cast<T>(0) : w / norm;
if (norm)
{
for (size_t i = 0; i < tempweights.size() && offset <= pixmap.height(); i++)
{
offset = std::min<T>(offset + tempweights[i] * pixmap.height(), pixmap.height());
painter.fillRect(0, start, pixmap.width(), offset, m_Fractorium->m_XformComboColors[i % XFORM_COLOR_COUNT]);
start = offset;
}
}
else
{
painter.fillRect(0, 0, pixmap.width(), pixmap.height(), m_Fractorium->m_XformComboColors[0]);
}
twi->setData(Qt::DecorationRole, pixmap);
m_Fractorium->ui.XaosDistVizTableWidget->setItem(0, i, twi.release());
}
}
m_Fractorium->ui.XaosDistVizTableWidget->resizeRowsToContents();
m_Fractorium->ui.XaosDistVizTableWidget->resizeColumnsToContents();
m_Fractorium->ui.XaosAppliedTableView->resizeRowsToContents();
m_Fractorium->ui.XaosAppliedTableView->resizeColumnsToContents();
}
/// <summary>
/// Set the xaos value.
/// Called when any xaos spinner is changed.
/// It actually gets called multiple times as the user clicks around the
/// xaos table due to how QTableView passes events to and from its model.
/// To filter out spurrious events, the value is checked against the existing
/// xaos value.
/// Resets the rendering process.
/// </summary>
/// <param name="x">The index of the xform whose xaos value was changed (column)</param>
/// <param name="y">The index of the to value that was changed for the xform (row)</param>
/// <param name="val">The changed value of the xaos element</param>
template <typename T>
void FractoriumEmberController<T>::XaosChanged(int x, int y, double val)
{
auto newVal = TruncPrecision(val, XAOS_PREC);//Sometimes 0 comes in as a very small number, so round.
if (auto xform = m_Ember.GetXform(x))
if (!IsClose<T>(newVal, xform->Xaos(y), T(1e-7)))
{
Update([&] { xform->SetXaos(y, newVal); });
FillAppliedXaos();
}
}
void Fractorium::OnXaosChanged(double d)
{
if (auto senderSpinBox = qobject_cast<DoubleSpinBox*>(sender()))
{
auto p = senderSpinBox->property("tableindex").toPoint();
m_Controller->XaosChanged(p.y(), p.x(), d);//Intentionally switched, column is the from xform, row is the to xform.
}
}
void Fractorium::OnXaosTableModelDataChanged(const QModelIndex& indexA, const QModelIndex& indexB)
{
m_Controller->XaosChanged(indexA.column(), indexA.row(), indexA.data().toDouble());//Intentionally switched, column is the from xform, row is the to xform.
}
/// <summary>
/// Clear xaos table, recreate all spinners based on the xaos used in the current ember.
/// </summary>
void Fractorium::FillXaosTable()
{
int count = static_cast<int>(m_Controller->XformCount());
QStringList hl, vl, blanks;
auto oldModel = std::make_unique<QStandardItemModel>(m_XaosTableModel);
hl.reserve(count);
vl.reserve(count);
blanks.push_back("");
m_XaosTableModel = new QStandardItemModel(count, count, this);
m_AppliedXaosTableModel = new QStandardItemModel(count, count, this);
connect(m_XaosTableModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(OnXaosTableModelDataChanged(QModelIndex, QModelIndex)));
for (int i = 0; i < count; i++)
{
auto s = m_Controller->MakeXformCaption(i);
hl.push_back("F" + s);
vl.push_back("T" + s);
}
m_XaosTableModel->setHorizontalHeaderLabels(hl);
m_XaosTableModel->setVerticalHeaderLabels(vl);
m_AppliedXaosTableModel->setHorizontalHeaderLabels(hl);
m_AppliedXaosTableModel->setVerticalHeaderLabels(vl);
ui.XaosDistVizTableWidget->setRowCount(1);
ui.XaosDistVizTableWidget->setColumnCount(count);
ui.XaosDistVizTableWidget->setHorizontalHeaderLabels(hl);
ui.XaosDistVizTableWidget->setVerticalHeaderLabels(blanks);
ui.XaosDistVizTableWidget->verticalHeader()->setSectionsClickable(false);
ui.XaosDistVizTableWidget->horizontalHeader()->setSectionsClickable(false);
ui.XaosTableView->setModel(m_XaosTableModel);
ui.XaosAppliedTableView->setModel(m_AppliedXaosTableModel);
ui.XaosTableView->setItemDelegate(m_XaosTableItemDelegate);//No need for a delegate on the applied table because it's read-only.
ui.XaosDistVizTableWidget->verticalHeader()->setFixedWidth(ui.XaosTableView->verticalHeader()->width());
SetTabOrder(this, ui.ClearXaosButton, ui.RandomXaosButton);
ui.XaosDistVizTableWidget->setRowHeight(0, ui.XaosTableView->rowHeight(0) * count);
m_Controller->FillXaos();
m_Controller->FillAppliedXaos();
//Needed to get the dark stylesheet to correctly color the top left corner button.
auto widgetList = ui.XaosTableView->findChildren<QAbstractButton*>();
for (auto& it : widgetList)
it->setEnabled(true);
widgetList = ui.XaosAppliedTableView->findChildren<QAbstractButton*>();
for (auto& it : widgetList)
it->setEnabled(true);
}
/// <summary>
/// Clear all xaos from the current ember.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ClearXaos()
{
UpdateAll([&](Ember<T>& ember, bool isMain)
{
ember.ClearXaos();
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
FillXaos();
FillAppliedXaos();
}
void Fractorium::OnClearXaosButtonClicked(bool checked) { m_Controller->ClearXaos(); }
/// <summary>
/// Set all xaos values to random numbers.
/// There is a 50% chance they're set to 0 or 1, and
/// 50% that they're 0-3.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::RandomXaos()
{
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
Update([&]
{
size_t i = 0;
while (auto xform = m_Ember.GetXform(i++))
{
for (size_t j = 0; j < m_Ember.XformCount(); j++)
{
if (!ctrl)
xform->SetXaos(j, static_cast<T>(m_Rand.RandBit()));
else if (m_Rand.RandBit())
xform->SetXaos(j, static_cast<T>(m_Rand.RandBit()));
else
xform->SetXaos(j, TruncPrecision(m_Rand.Frand<T>(0, 3), 3));
}
}
});
FillXaos();
FillAppliedXaos();
}
void Fractorium::OnRandomXaosButtonClicked(bool checked) { m_Controller->RandomXaos(); }
/// <summary>
/// Add a layer using the specified number of xforms.
/// A layer is defined as a new set of xforms whose xaos values are the following:
/// From existing to existing: unchanged.
/// From existing to new: 0.
/// From new to existing: 0.
/// From new to new: 1.
/// Resets the rendering process.
/// </summary>
/// <param name="xforms">The number of new xforms to add to create the layer</param>
template <typename T>
void FractoriumEmberController<T>::AddLayer(int xforms)
{
Update([&]
{
std::vector<std::pair<Xform<T>, size_t>> vec(xforms);
AddXformsWithXaos(m_Ember, vec, eXaosPasteStyle::ZERO_TO_ONE);
});
FillXforms();
FillSummary();
}
void Fractorium::OnAddLayerButtonClicked(bool checked) { m_Controller->AddLayer(ui.AddLayerSpinBox->value()); }
/// <summary>
/// Flip the row and column values of the xaos table.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::TransposeXaos()
{
Update([&]
{
size_t i = 0, j = 0;
vector<vector<double>> tempxaos;
tempxaos.reserve(m_Ember.XformCount());
while (const auto xform = m_Ember.GetXform(i++))
{
vector<double> tempvec;
tempvec.reserve(m_Ember.XformCount());
for (j = 0; j < m_Ember.XformCount(); j++)
tempvec.push_back(xform->Xaos(j));
tempxaos.push_back(std::move(tempvec));
}
for (j = 0; j < tempxaos.size(); j++)
for (i = 0; i < tempxaos[j].size(); i++)
if (auto xform = m_Ember.GetXform(i))
xform->SetXaos(j, static_cast<T>(tempxaos[j][i]));
});
FillXaos();
FillAppliedXaos();
}
void Fractorium::OnTransposeXaosButtonClicked(bool checked) { m_Controller->TransposeXaos(); }
/// <summary>
/// Toggle all xaos values in one row on left mouse button double click and resize all cells to fit their data.
/// Skip toggling and only refit on right mouse button double click.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::OnXaosRowDoubleClicked(int logicalIndex)
{
const auto btn = QApplication::mouseButtons();
if (!btn.testFlag(Qt::RightButton))
ToggleTableRow(ui.XaosTableView, logicalIndex);
ui.XaosTableView->resizeRowsToContents();
ui.XaosTableView->resizeColumnsToContents();
m_Controller->FillAppliedXaos();
}
/// <summary>
/// Toggle all xaos values in one column on left mouse button double click and resize all cells to fit their data.
/// Skip toggling and only refit on right mouse button double click.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the column that was double clicked</param>
void Fractorium::OnXaosColDoubleClicked(int logicalIndex)
{
const auto btn = QApplication::mouseButtons();
if (!btn.testFlag(Qt::RightButton))
ToggleTableCol(ui.XaosTableView, logicalIndex);
ui.XaosTableView->resizeRowsToContents();
ui.XaosTableView->resizeColumnsToContents();
m_Controller->FillAppliedXaos();
}
/// <summary>
/// Take the value of the horizontal scrollbar on the xaos table and set the same
/// horizontal scroll bar position on XaosDistVizTableWidget and XaosAppliedTableView.
/// This allows them to easily see the same part of all three tables at the same time
/// when there are more xforms than can fit on the screen at once.
/// </summary>
/// <param name="value">The value of the xaos table horizontal scroll bar</param>
void Fractorium::OnXaosHScrollValueChanged(int value)
{
ui.XaosDistVizTableWidget->horizontalScrollBar()->setValue(value);
ui.XaosAppliedTableView->horizontalScrollBar()->setValue(value);
}
/// <summary>
/// Take the value of the vertical scrollbar on the xaos table and set the same
/// vertical scroll bar position on XaosDistVizTableWidget and XaosAppliedTableView.
/// This allows them to easily see the same part of all three tables at the same time
/// when there are more xforms than can fit on the screen at once.
/// </summary>
/// <param name="value">The value of the xaos table vertical scroll bar</param>
void Fractorium::OnXaosVScrollValueChanged(int value)
{
ui.XaosDistVizTableWidget->verticalScrollBar()->setValue(value);
ui.XaosAppliedTableView->verticalScrollBar()->setValue(value);
}
/// <summary>
/// Get an enum value corresponding to the currently selected xaos pasting mode.
/// </summary>
/// <returns>The xaos pasting mode enum</returns>
eXaosPasteStyle Fractorium::GetXaosPasteStyleType()
{
if (ui.XaosPasteNoneRadio->isChecked())
return eXaosPasteStyle::NONE;
else if (ui.XaosPaste0to1Radio->isChecked())
return eXaosPasteStyle::ZERO_TO_ONE;
else if (ui.XaosPaste0toValsRadio->isChecked())
return eXaosPasteStyle::ZERO_TO_VALS;
else if (ui.XaosPaste1toValsRadio->isChecked())
return eXaosPasteStyle::ONE_TO_VALS;
else if (ui.XaosPasteValsTo1Radio->isChecked())
return eXaosPasteStyle::VALS_TO_ONE;
else
return eXaosPasteStyle::NONE;
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif
#include "FractoriumPch.h"
#include "Fractorium.h"
#define XAOS_PREC 6
/// <summary>
/// Initialize the xforms xaos UI.
/// </summary>
void Fractorium::InitXaosUI()
{
int spinHeight = 20;
ui.XaosTableView->verticalHeader()->setSectionsClickable(true);
ui.XaosTableView->horizontalHeader()->setSectionsClickable(true);
m_XaosSpinBox = new DoubleSpinBox(nullptr, spinHeight, 0.1, false);
m_XaosSpinBox->DoubleClick(true);
m_XaosSpinBox->DoubleClickZero(1);
m_XaosSpinBox->DoubleClickNonZero(0);
m_XaosSpinBox->setDecimals(XAOS_PREC);
m_XaosSpinBox->setObjectName("XaosSpinBox");
m_XaosTableModel = nullptr;
m_AppliedXaosTableModel = nullptr;
m_XaosTableItemDelegate = new DoubleSpinBoxTableItemDelegate(m_XaosSpinBox, this);
connect(m_XaosSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnXaosChanged(double)), Qt::QueuedConnection);
connect(ui.ClearXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.RandomXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.TransposeXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnTransposeXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.AddLayerButton, SIGNAL(clicked(bool)), this, SLOT(OnAddLayerButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.XaosTableView->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosRowDoubleClicked(int)), Qt::QueuedConnection);
connect(ui.XaosTableView->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosColDoubleClicked(int)), Qt::QueuedConnection);
connect(ui.XaosTableView->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(OnXaosHScrollValueChanged(int)), Qt::QueuedConnection);
connect(ui.XaosTableView->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(OnXaosVScrollValueChanged(int)), Qt::QueuedConnection);
}
/// <summary>
/// Fill the xaos table with the values from the ember.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillXaos()
{
for (int i = 0, count = static_cast<int>(XformCount()); i < count; i++)//Column.
{
if (const auto xform = m_Ember.GetXform(i))
{
for (int j = 0; j < count; j++)//Row.
{
QModelIndex index = m_Fractorium->m_XaosTableModel->index(j, i, QModelIndex());//j and i are intentionally swapped here.
m_Fractorium->m_XaosTableModel->setData(index, xform->Xaos(j));
}
}
}
m_Fractorium->ui.XaosTableView->resizeRowsToContents();
m_Fractorium->ui.XaosTableView->resizeColumnsToContents();
}
/// <summary>
/// Fill the xaos table with the xaos values applied to the xform weights from the ember.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillAppliedXaos()
{
m_Ember.CalcNormalizedWeights(m_NormalizedWeights);
for (int i = 0, count = int(XformCount()); i < count; i++)//Column.
{
if (const auto xform = m_Ember.GetXform(i))
{
T norm = 0;
double start = 0, offset = 0;
auto tempweights = m_NormalizedWeights;
for (int j = 0; j < count; j++)//Row.
{
tempweights[j] *= xform->Xaos(j);
QModelIndex index = m_Fractorium->m_AppliedXaosTableModel->index(j, i, QModelIndex());//j and i are intentionally swapped here.
m_Fractorium->m_AppliedXaosTableModel->setData(index, TruncPrecision(xform->Xaos(j) * xform->m_Weight, 4));//Applied xaos is just a read only table for display purposes.
}
QPixmap pixmap(m_Fractorium->ui.XaosAppliedTableView->columnWidth(i) - 8, m_Fractorium->ui.XaosTableView->rowHeight(0) * count);
QPainter painter(&pixmap);
auto twi = std::make_unique<QTableWidgetItem>();
for (auto& w : tempweights) norm += w;
for (auto& w : tempweights) w = norm == static_cast<T>(0) ? static_cast<T>(0) : w / norm;
if (norm)
{
for (size_t i = 0; i < tempweights.size() && offset <= pixmap.height(); i++)
{
offset = std::min<T>(offset + tempweights[i] * pixmap.height(), pixmap.height());
painter.fillRect(0, start, pixmap.width(), offset, m_Fractorium->m_XformComboColors[i % XFORM_COLOR_COUNT]);
start = offset;
}
}
else
{
painter.fillRect(0, 0, pixmap.width(), pixmap.height(), m_Fractorium->m_XformComboColors[0]);
}
twi->setData(Qt::DecorationRole, pixmap);
m_Fractorium->ui.XaosDistVizTableWidget->setItem(0, i, twi.release());
}
}
m_Fractorium->ui.XaosDistVizTableWidget->resizeRowsToContents();
m_Fractorium->ui.XaosDistVizTableWidget->resizeColumnsToContents();
m_Fractorium->ui.XaosAppliedTableView->resizeRowsToContents();
m_Fractorium->ui.XaosAppliedTableView->resizeColumnsToContents();
}
/// <summary>
/// Set the xaos value.
/// Called when any xaos spinner is changed.
/// It actually gets called multiple times as the user clicks around the
/// xaos table due to how QTableView passes events to and from its model.
/// To filter out spurrious events, the value is checked against the existing
/// xaos value.
/// Resets the rendering process.
/// </summary>
/// <param name="x">The index of the xform whose xaos value was changed (column)</param>
/// <param name="y">The index of the to value that was changed for the xform (row)</param>
/// <param name="val">The changed value of the xaos element</param>
template <typename T>
void FractoriumEmberController<T>::XaosChanged(int x, int y, double val)
{
auto newVal = TruncPrecision(val, XAOS_PREC);//Sometimes 0 comes in as a very small number, so round.
if (auto xform = m_Ember.GetXform(x))
if (!IsClose<T>(newVal, xform->Xaos(y), T(1e-7)))
{
Update([&] { xform->SetXaos(y, newVal); });
FillAppliedXaos();
}
}
void Fractorium::OnXaosChanged(double d)
{
if (auto senderSpinBox = qobject_cast<DoubleSpinBox*>(sender()))
{
auto p = senderSpinBox->property("tableindex").toPoint();
m_Controller->XaosChanged(p.y(), p.x(), d);//Intentionally switched, column is the from xform, row is the to xform.
}
}
void Fractorium::OnXaosTableModelDataChanged(const QModelIndex& indexA, const QModelIndex& indexB)
{
m_Controller->XaosChanged(indexA.column(), indexA.row(), indexA.data().toDouble());//Intentionally switched, column is the from xform, row is the to xform.
}
/// <summary>
/// Clear xaos table, recreate all spinners based on the xaos used in the current ember.
/// </summary>
void Fractorium::FillXaosTable()
{
int count = static_cast<int>(m_Controller->XformCount());
QStringList hl, vl, blanks;
auto oldModel = std::make_unique<QStandardItemModel>(m_XaosTableModel);
hl.reserve(count);
vl.reserve(count);
blanks.push_back("");
m_XaosTableModel = new QStandardItemModel(count, count, this);
m_AppliedXaosTableModel = new QStandardItemModel(count, count, this);
connect(m_XaosTableModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(OnXaosTableModelDataChanged(QModelIndex, QModelIndex)));
for (int i = 0; i < count; i++)
{
auto s = m_Controller->MakeXformCaption(i);
hl.push_back("F" + s);
vl.push_back("T" + s);
}
m_XaosTableModel->setHorizontalHeaderLabels(hl);
m_XaosTableModel->setVerticalHeaderLabels(vl);
m_AppliedXaosTableModel->setHorizontalHeaderLabels(hl);
m_AppliedXaosTableModel->setVerticalHeaderLabels(vl);
ui.XaosDistVizTableWidget->setRowCount(1);
ui.XaosDistVizTableWidget->setColumnCount(count);
ui.XaosDistVizTableWidget->setHorizontalHeaderLabels(hl);
ui.XaosDistVizTableWidget->setVerticalHeaderLabels(blanks);
ui.XaosDistVizTableWidget->verticalHeader()->setSectionsClickable(false);
ui.XaosDistVizTableWidget->horizontalHeader()->setSectionsClickable(false);
ui.XaosTableView->setModel(m_XaosTableModel);
ui.XaosAppliedTableView->setModel(m_AppliedXaosTableModel);
ui.XaosTableView->setItemDelegate(m_XaosTableItemDelegate);//No need for a delegate on the applied table because it's read-only.
ui.XaosDistVizTableWidget->verticalHeader()->setFixedWidth(ui.XaosTableView->verticalHeader()->width());
SetTabOrder(this, ui.ClearXaosButton, ui.RandomXaosButton);
ui.XaosDistVizTableWidget->setRowHeight(0, ui.XaosTableView->rowHeight(0) * count);
m_Controller->FillXaos();
m_Controller->FillAppliedXaos();
//Needed to get the dark stylesheet to correctly color the top left corner button.
auto widgetList = ui.XaosTableView->findChildren<QAbstractButton*>();
for (auto& it : widgetList)
it->setEnabled(true);
widgetList = ui.XaosAppliedTableView->findChildren<QAbstractButton*>();
for (auto& it : widgetList)
it->setEnabled(true);
}
/// <summary>
/// Clear all xaos from the current ember.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ClearXaos()
{
UpdateAll([&](Ember<T>& ember, bool isMain)
{
ember.ClearXaos();
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
FillXaos();
FillAppliedXaos();
}
void Fractorium::OnClearXaosButtonClicked(bool checked) { m_Controller->ClearXaos(); }
/// <summary>
/// Set all xaos values to random numbers.
/// There is a 50% chance they're set to 0 or 1, and
/// 50% that they're 0-3.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::RandomXaos()
{
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
Update([&]
{
size_t i = 0;
while (auto xform = m_Ember.GetXform(i++))
{
for (size_t j = 0; j < m_Ember.XformCount(); j++)
{
if (!ctrl)
xform->SetXaos(j, static_cast<T>(m_Rand.RandBit()));
else if (m_Rand.RandBit())
xform->SetXaos(j, static_cast<T>(m_Rand.RandBit()));
else
xform->SetXaos(j, TruncPrecision(m_Rand.Frand<T>(0, 3), 3));
}
}
});
FillXaos();
FillAppliedXaos();
}
void Fractorium::OnRandomXaosButtonClicked(bool checked) { m_Controller->RandomXaos(); }
/// <summary>
/// Add a layer using the specified number of xforms.
/// A layer is defined as a new set of xforms whose xaos values are the following:
/// From existing to existing: unchanged.
/// From existing to new: 0.
/// From new to existing: 0.
/// From new to new: 1.
/// Resets the rendering process.
/// </summary>
/// <param name="xforms">The number of new xforms to add to create the layer</param>
template <typename T>
void FractoriumEmberController<T>::AddLayer(int xforms)
{
Update([&]
{
std::vector<std::pair<Xform<T>, size_t>> vec(xforms);
AddXformsWithXaos(m_Ember, vec, eXaosPasteStyle::ZERO_TO_ONE);
});
FillXforms();
FillSummary();
}
void Fractorium::OnAddLayerButtonClicked(bool checked) { m_Controller->AddLayer(ui.AddLayerSpinBox->value()); }
/// <summary>
/// Flip the row and column values of the xaos table.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::TransposeXaos()
{
Update([&]
{
size_t i = 0, j = 0;
vector<vector<double>> tempxaos;
tempxaos.reserve(m_Ember.XformCount());
while (const auto xform = m_Ember.GetXform(i++))
{
vector<double> tempvec;
tempvec.reserve(m_Ember.XformCount());
for (j = 0; j < m_Ember.XformCount(); j++)
tempvec.push_back(xform->Xaos(j));
tempxaos.push_back(std::move(tempvec));
}
for (j = 0; j < tempxaos.size(); j++)
for (i = 0; i < tempxaos[j].size(); i++)
if (auto xform = m_Ember.GetXform(i))
xform->SetXaos(j, static_cast<T>(tempxaos[j][i]));
});
FillXaos();
FillAppliedXaos();
}
void Fractorium::OnTransposeXaosButtonClicked(bool checked) { m_Controller->TransposeXaos(); }
/// <summary>
/// Toggle all xaos values in one row on left mouse button double click and resize all cells to fit their data.
/// Skip toggling and only refit on right mouse button double click.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the row that was double clicked</param>
void Fractorium::OnXaosRowDoubleClicked(int logicalIndex)
{
const auto btn = QApplication::mouseButtons();
if (!btn.testFlag(Qt::RightButton))
ToggleTableRow(ui.XaosTableView, logicalIndex);
ui.XaosTableView->resizeRowsToContents();
ui.XaosTableView->resizeColumnsToContents();
m_Controller->FillAppliedXaos();
}
/// <summary>
/// Toggle all xaos values in one column on left mouse button double click and resize all cells to fit their data.
/// Skip toggling and only refit on right mouse button double click.
/// Resets the rendering process.
/// </summary>
/// <param name="logicalIndex">The index of the column that was double clicked</param>
void Fractorium::OnXaosColDoubleClicked(int logicalIndex)
{
const auto btn = QApplication::mouseButtons();
if (!btn.testFlag(Qt::RightButton))
ToggleTableCol(ui.XaosTableView, logicalIndex);
ui.XaosTableView->resizeRowsToContents();
ui.XaosTableView->resizeColumnsToContents();
m_Controller->FillAppliedXaos();
}
/// <summary>
/// Take the value of the horizontal scrollbar on the xaos table and set the same
/// horizontal scroll bar position on XaosDistVizTableWidget and XaosAppliedTableView.
/// This allows them to easily see the same part of all three tables at the same time
/// when there are more xforms than can fit on the screen at once.
/// </summary>
/// <param name="value">The value of the xaos table horizontal scroll bar</param>
void Fractorium::OnXaosHScrollValueChanged(int value)
{
ui.XaosDistVizTableWidget->horizontalScrollBar()->setValue(value);
ui.XaosAppliedTableView->horizontalScrollBar()->setValue(value);
}
/// <summary>
/// Take the value of the vertical scrollbar on the xaos table and set the same
/// vertical scroll bar position on XaosDistVizTableWidget and XaosAppliedTableView.
/// This allows them to easily see the same part of all three tables at the same time
/// when there are more xforms than can fit on the screen at once.
/// </summary>
/// <param name="value">The value of the xaos table vertical scroll bar</param>
void Fractorium::OnXaosVScrollValueChanged(int value)
{
ui.XaosDistVizTableWidget->verticalScrollBar()->setValue(value);
ui.XaosAppliedTableView->verticalScrollBar()->setValue(value);
}
/// <summary>
/// Get an enum value corresponding to the currently selected xaos pasting mode.
/// </summary>
/// <returns>The xaos pasting mode enum</returns>
eXaosPasteStyle Fractorium::GetXaosPasteStyleType()
{
if (ui.XaosPasteNoneRadio->isChecked())
return eXaosPasteStyle::NONE;
else if (ui.XaosPaste0to1Radio->isChecked())
return eXaosPasteStyle::ZERO_TO_ONE;
else if (ui.XaosPaste0toValsRadio->isChecked())
return eXaosPasteStyle::ZERO_TO_VALS;
else if (ui.XaosPaste1toValsRadio->isChecked())
return eXaosPasteStyle::ONE_TO_VALS;
else if (ui.XaosPasteValsTo1Radio->isChecked())
return eXaosPasteStyle::VALS_TO_ONE;
else
return eXaosPasteStyle::NONE;
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,277 +1,277 @@
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the xforms color UI.
/// </summary>
void Fractorium::InitXformsColorUI()
{
const auto spinHeight = 20;
auto row = 0;
m_XformColorValueItem = new QTableWidgetItem();
//Can't set this in the designer, so do it here.
m_XformColorValueItem->setToolTip("The index in the palette the current xform uses.\r\n\r\n"
"This value can be changed by scrolling the mouse wheel in the box displaying the value or by dragging the scroll bar.");
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);
connect(ui.RandomColorIndicesButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomColorIndicesButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.ToggleColorIndicesButton, SIGNAL(clicked(bool)), this, SLOT(OnToggleColorIndicesButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.RandomColorSpeedButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomColorSpeedButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.ToggleColorSpeedsButton, SIGNAL(clicked(bool)), this, SLOT(OnToggleColorSpeedsButtonClicked(bool)), Qt::QueuedConnection);
SetupSpinner<DoubleSpinBox, double>(ui.XformColorIndexTable, this, row, 1, m_XformColorIndexSpin, spinHeight, 0, 1, 0.01, SIGNAL(valueChanged(double)), SLOT(OnXformColorIndexChanged(double)), false, 0, 1, 0);
SetupSpinner<DoubleSpinBox, double>(ui.XformColorValuesTable, this, row, 1, m_XformColorSpeedSpin, spinHeight, -1, 1, 0.1, SIGNAL(valueChanged(double)), SLOT(OnXformColorSpeedChanged(double)), true, 0.5, 0.5, 0);
SetupSpinner<DoubleSpinBox, double>(ui.XformColorValuesTable, this, row, 1, m_XformOpacitySpin, spinHeight, 0, 100, 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 selected xforms.
/// 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>
/// <param name="updateSpinner">True to update the color index spinner with the new value, else don't.</param>
/// <param name="updateScroll">True to update the color index scroll bar with the new value, else don't.</param>
/// <param name="update">The type of updating to do, default: eXformUpdate::UPDATE_SELECTED.</param>
/// <param name="index">The index of the xform to update. Ignored unless update is eXformUpdate::UPDATE_SPECIFIC. Default: 0.</param>
template <typename T>
void FractoriumEmberController<T>::XformColorIndexChanged(double d, bool updateRender, bool updateSpinner, bool updateScroll, eXformUpdate update, size_t index)
{
const auto updateGUI = update != eXformUpdate::UPDATE_SPECIFIC || index == m_Fractorium->ui.CurrentXformCombo->currentIndex();
if (updateRender)//False when just updating GUI in response to a change elsewhere, true when in response to a GUI change so update values and reset renderer.
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
{
xform->m_ColorX = Clamp<T>(d, 0, 1);
}, update, updateRender, eProcessAction::FULL_RENDER, index);
}
//Only do this is coming from elsewhere, like the palette editor. Otherwise, normal events will handle updating the spinner.
if (updateSpinner && updateGUI)
{
m_Fractorium->m_XformColorIndexSpin->SetValueStealth(CurrentXform()->m_ColorX);
}
if (updateScroll && updateGUI)
{
const auto scroll = m_Fractorium->ui.XformColorScroll;
const auto scrollVal = d * scroll->maximum();
scroll->blockSignals(true);
scroll->setValue(scrollVal);
scroll->blockSignals(false);
}
if (updateGUI)
m_Fractorium->ui.XformColorIndexTable->item(0, 0)->setBackgroundColor(ColorIndexToQColor(d));//Grab the current color from the index and assign it to the first cell of the first table.
}
void Fractorium::OnXformColorIndexChanged(double d) { OnXformColorIndexChanged(d, true, false, true, eXformUpdate::UPDATE_SELECTED, std::numeric_limits<size_t>::max()); }
void Fractorium::OnXformColorIndexChanged(double d, bool updateRender, bool updateSpinner, bool updateScroll, eXformUpdate update, size_t index) { m_Controller->XformColorIndexChanged(d, updateRender, updateSpinner, updateScroll, update, index); }
/// <summary>
/// Set the color index of the current xform.
/// Will trigger an update which will cause 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>
void Fractorium::OnXformScrollColorIndexChanged(int d)
{
OnXformColorIndexChanged(d / static_cast<double>(ui.XformColorScroll->maximum()), true, true, false);//Update spinner, but not scrollbar. Trigger render update.
}
/// <summary>
/// Set all xform color indices to a random value between 0 and 1, inclusive.
/// Called when the Random Indices button is clicked.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::RandomColorIndicesButtonClicked()
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorX = m_Rand.Frand01<T>(); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here...
m_Fractorium->m_XformColorIndexSpin->setValue(CurrentXform()->m_ColorX);//...do it via GUI. This will set scrollbar value as well.
}
void Fractorium::OnRandomColorIndicesButtonClicked(bool b) { m_Controller->RandomColorIndicesButtonClicked(); }
/// <summary>
/// Resets the rendering process.
/// Set all xform color indices to either 0 and 1, sequentially toggling.
/// Called when the Toggle Indices button is clicked.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ToggleColorIndicesButtonClicked()
{
char ch = 1;
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorX = static_cast<T>(ch ^= 1); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here...
m_Fractorium->m_XformColorIndexSpin->setValue(CurrentXform()->m_ColorX);//...do it via GUI. This will set scrollbar value as well.
}
void Fractorium::OnToggleColorIndicesButtonClicked(bool b) { m_Controller->ToggleColorIndicesButtonClicked(); }
/// <summary>
/// Set all xform color speeds to a random value between 0 and 1, inclusive.
/// Called when the Random Color Speed button is clicked.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::RandomColorSpeedButtonClicked()
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = m_Rand.Frand01<T>(); }, eXformUpdate::UPDATE_ALL);
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed);
}
void Fractorium::OnRandomColorSpeedButtonClicked(bool b) { m_Controller->RandomColorSpeedButtonClicked(); }
/// <summary>
/// Set all xform color speeds to either 0 and 0.5, sequentially toggling.
/// Called when the Toggle Color Speed button is clicked.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ToggleColorSpeedsButtonClicked()
{
char ch = 1;
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = static_cast<T>((ch ^= 1) ? 0.5 : 0.0); }, eXformUpdate::UPDATE_ALL);
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed);
}
void Fractorium::OnToggleColorSpeedsButtonClicked(bool b) { m_Controller->ToggleColorSpeedsButtonClicked(); }
/// <summary>
/// Set the color speed of the selected xforms.
/// 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) { UpdateXform([&] (Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = d; }, eXformUpdate::UPDATE_SELECTED); }
void Fractorium::OnXformColorSpeedChanged(double d) { m_Controller->XformColorSpeedChanged(d); }
/// <summary>
/// Set the opacity of the selected xforms.
/// 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) { UpdateXform([&] (Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_Opacity = d; }, eXformUpdate::UPDATE_SELECTED); }
void Fractorium::OnXformOpacityChanged(double d) { m_Controller->XformOpacityChanged(d); }
/// <summary>
/// Set the direct color percentage of the selected xforms.
/// 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) { UpdateXform([&] (Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_DirectColor = d; }, eXformUpdate::UPDATE_SELECTED); }
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 opacities of ember, as the temporary opacity values will be applied
/// right before rendering and reset right after.
/// Called when solo xform check box is checked.
/// Resets the rendering process.
/// </summary>
/// <param name="state">The state of the checkbox</param>
/// <param name="index">The index which has been specified as the solo xform, -1 to specify none.</param>
template <typename T>
void FractoriumEmberController<T>::SoloXformCheckBoxStateChanged(int state, int index)
{
if (state == Qt::Checked)
{
m_Ember.m_Solo = index;
m_Fractorium->ui.SoloXformCheckBox->setText("Solo (" + ToString(index + 1) + ")");
}
else if (state == Qt::Unchecked)
{
m_Ember.m_Solo = -1;
m_Fractorium->ui.SoloXformCheckBox->setText("Solo");
}
UpdateRender();
}
void Fractorium::OnSoloXformCheckBoxStateChanged(int state) { m_Controller->SoloXformCheckBoxStateChanged(state, ui.CurrentXformCombo->currentIndex()); }
/// <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)
{
QPixmap pixmap(QPixmap::fromImage(m_Controller->FinalPaletteImage()));
SetPaletteTableItem(&pixmap, ui.XformPaletteRefTable, m_PaletteRefItem, 0, 0);
}
/// <summary>
/// Look up the passed in index in the current ember's palette
/// and return the QColor equivalent.
/// </summary>
/// <param name="d">The palette index to look up, 0-1.</param>
/// <returns>The palette color at the given index as a QColor</returns>
template <typename T>
QColor FractoriumEmberController<T>::ColorIndexToQColor(double d)
{
v4F entry = m_Ember.m_Palette[Clamp<size_t>(d * (m_Ember.m_Palette.Size() - 1), 0, m_Ember.m_Palette.Size())];
entry.r *= 255;
entry.g *= 255;
entry.b *= 255;
QRgb rgb = static_cast<uint>(entry.r) << 16 | static_cast<uint>(entry.g) << 8 | static_cast<uint>(entry.b);
return QColor::fromRgb(rgb);
}
/// <summary>
/// Set the color index, speed and opacity spinners with the values of the passed in 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);//Probably ought to put scroll bar update here too.
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, false, true);//Had to call stealth before to avoid doing an update, now manually update related controls, still without doing an update.
}
/// <summary>
/// Set the cell at the row and column in the passed in table to the passed in pixmap.
/// </summary>
/// <param name="pixmap">The pixmap to assign</param>
/// <param name="table">The table whose cell will be filled with the image</param>
/// <param name="item">The QTableWidgetItem in the cell</param>
/// <param name="row">The row of the cell</param>
/// <param name="col">The column of the cell</param>
void Fractorium::SetPaletteTableItem(QPixmap* pixmap, QTableWidget* table, QTableWidgetItem* item, int row, int col)
{
if (pixmap && !pixmap->isNull())
{
const QSize size(table->columnWidth(col), table->rowHeight(row) + 1);
item->setData(Qt::DecorationRole, pixmap->scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the xforms color UI.
/// </summary>
void Fractorium::InitXformsColorUI()
{
const auto spinHeight = 20;
auto row = 0;
m_XformColorValueItem = new QTableWidgetItem();
//Can't set this in the designer, so do it here.
m_XformColorValueItem->setToolTip("The index in the palette the current xform uses.\r\n\r\n"
"This value can be changed by scrolling the mouse wheel in the box displaying the value or by dragging the scroll bar.");
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);
connect(ui.RandomColorIndicesButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomColorIndicesButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.ToggleColorIndicesButton, SIGNAL(clicked(bool)), this, SLOT(OnToggleColorIndicesButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.RandomColorSpeedButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomColorSpeedButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.ToggleColorSpeedsButton, SIGNAL(clicked(bool)), this, SLOT(OnToggleColorSpeedsButtonClicked(bool)), Qt::QueuedConnection);
SetupSpinner<DoubleSpinBox, double>(ui.XformColorIndexTable, this, row, 1, m_XformColorIndexSpin, spinHeight, 0, 1, 0.01, SIGNAL(valueChanged(double)), SLOT(OnXformColorIndexChanged(double)), false, 0, 1, 0);
SetupSpinner<DoubleSpinBox, double>(ui.XformColorValuesTable, this, row, 1, m_XformColorSpeedSpin, spinHeight, -1, 1, 0.1, SIGNAL(valueChanged(double)), SLOT(OnXformColorSpeedChanged(double)), true, 0.5, 0.5, 0);
SetupSpinner<DoubleSpinBox, double>(ui.XformColorValuesTable, this, row, 1, m_XformOpacitySpin, spinHeight, 0, 100, 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 selected xforms.
/// 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>
/// <param name="updateSpinner">True to update the color index spinner with the new value, else don't.</param>
/// <param name="updateScroll">True to update the color index scroll bar with the new value, else don't.</param>
/// <param name="update">The type of updating to do, default: eXformUpdate::UPDATE_SELECTED.</param>
/// <param name="index">The index of the xform to update. Ignored unless update is eXformUpdate::UPDATE_SPECIFIC. Default: 0.</param>
template <typename T>
void FractoriumEmberController<T>::XformColorIndexChanged(double d, bool updateRender, bool updateSpinner, bool updateScroll, eXformUpdate update, size_t index)
{
const auto updateGUI = update != eXformUpdate::UPDATE_SPECIFIC || index == m_Fractorium->ui.CurrentXformCombo->currentIndex();
if (updateRender)//False when just updating GUI in response to a change elsewhere, true when in response to a GUI change so update values and reset renderer.
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
{
xform->m_ColorX = Clamp<T>(d, 0, 1);
}, update, updateRender, eProcessAction::FULL_RENDER, index);
}
//Only do this is coming from elsewhere, like the palette editor. Otherwise, normal events will handle updating the spinner.
if (updateSpinner && updateGUI)
{
m_Fractorium->m_XformColorIndexSpin->SetValueStealth(CurrentXform()->m_ColorX);
}
if (updateScroll && updateGUI)
{
const auto scroll = m_Fractorium->ui.XformColorScroll;
const auto scrollVal = d * scroll->maximum();
scroll->blockSignals(true);
scroll->setValue(scrollVal);
scroll->blockSignals(false);
}
if (updateGUI)
m_Fractorium->ui.XformColorIndexTable->item(0, 0)->setBackground(ColorIndexToQColor(d));//Grab the current color from the index and assign it to the first cell of the first table.
}
void Fractorium::OnXformColorIndexChanged(double d) { OnXformColorIndexChanged(d, true, false, true, eXformUpdate::UPDATE_SELECTED, std::numeric_limits<size_t>::max()); }
void Fractorium::OnXformColorIndexChanged(double d, bool updateRender, bool updateSpinner, bool updateScroll, eXformUpdate update, size_t index) { m_Controller->XformColorIndexChanged(d, updateRender, updateSpinner, updateScroll, update, index); }
/// <summary>
/// Set the color index of the current xform.
/// Will trigger an update which will cause 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>
void Fractorium::OnXformScrollColorIndexChanged(int d)
{
OnXformColorIndexChanged(d / static_cast<double>(ui.XformColorScroll->maximum()), true, true, false);//Update spinner, but not scrollbar. Trigger render update.
}
/// <summary>
/// Set all xform color indices to a random value between 0 and 1, inclusive.
/// Called when the Random Indices button is clicked.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::RandomColorIndicesButtonClicked()
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorX = m_Rand.Frand01<T>(); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here...
m_Fractorium->m_XformColorIndexSpin->setValue(CurrentXform()->m_ColorX);//...do it via GUI. This will set scrollbar value as well.
}
void Fractorium::OnRandomColorIndicesButtonClicked(bool b) { m_Controller->RandomColorIndicesButtonClicked(); }
/// <summary>
/// Resets the rendering process.
/// Set all xform color indices to either 0 and 1, sequentially toggling.
/// Called when the Toggle Indices button is clicked.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ToggleColorIndicesButtonClicked()
{
char ch = 1;
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorX = static_cast<T>(ch ^= 1); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here...
m_Fractorium->m_XformColorIndexSpin->setValue(CurrentXform()->m_ColorX);//...do it via GUI. This will set scrollbar value as well.
}
void Fractorium::OnToggleColorIndicesButtonClicked(bool b) { m_Controller->ToggleColorIndicesButtonClicked(); }
/// <summary>
/// Set all xform color speeds to a random value between 0 and 1, inclusive.
/// Called when the Random Color Speed button is clicked.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::RandomColorSpeedButtonClicked()
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = m_Rand.Frand01<T>(); }, eXformUpdate::UPDATE_ALL);
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed);
}
void Fractorium::OnRandomColorSpeedButtonClicked(bool b) { m_Controller->RandomColorSpeedButtonClicked(); }
/// <summary>
/// Set all xform color speeds to either 0 and 0.5, sequentially toggling.
/// Called when the Toggle Color Speed button is clicked.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ToggleColorSpeedsButtonClicked()
{
char ch = 1;
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = static_cast<T>((ch ^= 1) ? 0.5 : 0.0); }, eXformUpdate::UPDATE_ALL);
m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed);
}
void Fractorium::OnToggleColorSpeedsButtonClicked(bool b) { m_Controller->ToggleColorSpeedsButtonClicked(); }
/// <summary>
/// Set the color speed of the selected xforms.
/// 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) { UpdateXform([&] (Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = d; }, eXformUpdate::UPDATE_SELECTED); }
void Fractorium::OnXformColorSpeedChanged(double d) { m_Controller->XformColorSpeedChanged(d); }
/// <summary>
/// Set the opacity of the selected xforms.
/// 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) { UpdateXform([&] (Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_Opacity = d; }, eXformUpdate::UPDATE_SELECTED); }
void Fractorium::OnXformOpacityChanged(double d) { m_Controller->XformOpacityChanged(d); }
/// <summary>
/// Set the direct color percentage of the selected xforms.
/// 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) { UpdateXform([&] (Xform<T>* xform, size_t xfindex, size_t selIndex) { xform->m_DirectColor = d; }, eXformUpdate::UPDATE_SELECTED); }
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 opacities of ember, as the temporary opacity values will be applied
/// right before rendering and reset right after.
/// Called when solo xform check box is checked.
/// Resets the rendering process.
/// </summary>
/// <param name="state">The state of the checkbox</param>
/// <param name="index">The index which has been specified as the solo xform, -1 to specify none.</param>
template <typename T>
void FractoriumEmberController<T>::SoloXformCheckBoxStateChanged(int state, int index)
{
if (state == Qt::Checked)
{
m_Ember.m_Solo = index;
m_Fractorium->ui.SoloXformCheckBox->setText("Solo (" + ToString(index + 1) + ")");
}
else if (state == Qt::Unchecked)
{
m_Ember.m_Solo = -1;
m_Fractorium->ui.SoloXformCheckBox->setText("Solo");
}
UpdateRender();
}
void Fractorium::OnSoloXformCheckBoxStateChanged(int state) { m_Controller->SoloXformCheckBoxStateChanged(state, ui.CurrentXformCombo->currentIndex()); }
/// <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)
{
QPixmap pixmap(QPixmap::fromImage(m_Controller->FinalPaletteImage()));
SetPaletteTableItem(&pixmap, ui.XformPaletteRefTable, m_PaletteRefItem, 0, 0);
}
/// <summary>
/// Look up the passed in index in the current ember's palette
/// and return the QColor equivalent.
/// </summary>
/// <param name="d">The palette index to look up, 0-1.</param>
/// <returns>The palette color at the given index as a QColor</returns>
template <typename T>
QColor FractoriumEmberController<T>::ColorIndexToQColor(double d)
{
v4F entry = m_Ember.m_Palette[Clamp<size_t>(d * (m_Ember.m_Palette.Size() - 1), 0, m_Ember.m_Palette.Size())];
entry.r *= 255;
entry.g *= 255;
entry.b *= 255;
QRgb rgb = static_cast<uint>(entry.r) << 16 | static_cast<uint>(entry.g) << 8 | static_cast<uint>(entry.b);
return QColor::fromRgb(rgb);
}
/// <summary>
/// Set the color index, speed and opacity spinners with the values of the passed in 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);//Probably ought to put scroll bar update here too.
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, false, true);//Had to call stealth before to avoid doing an update, now manually update related controls, still without doing an update.
}
/// <summary>
/// Set the cell at the row and column in the passed in table to the passed in pixmap.
/// </summary>
/// <param name="pixmap">The pixmap to assign</param>
/// <param name="table">The table whose cell will be filled with the image</param>
/// <param name="item">The QTableWidgetItem in the cell</param>
/// <param name="row">The row of the cell</param>
/// <param name="col">The column of the cell</param>
void Fractorium::SetPaletteTableItem(QPixmap* pixmap, QTableWidget* table, QTableWidgetItem* item, int row, int col)
{
if (pixmap && !pixmap->isNull())
{
const QSize size(table->columnWidth(col), table->rowHeight(row) + 1);
item->setData(Qt::DecorationRole, pixmap->scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif

View File

@ -1,152 +1,152 @@
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the xforms selection UI.
/// </summary>
void Fractorium::InitXformsSelectUI()
{
m_XformsSelectionLayout = dynamic_cast<QFormLayout*>(ui.XformsSelectGroupBoxScrollAreaWidget->layout());
connect(ui.XformsSelectAllButton, SIGNAL(clicked(bool)), this, SLOT(OnXformsSelectAllButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.XformsSelectNoneButton, SIGNAL(clicked(bool)), this, SLOT(OnXformsSelectNoneButtonClicked(bool)), Qt::QueuedConnection);
ClearXformsSelections();
}
/// <summary>
/// Check all of the xform selection checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnXformsSelectAllButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w, bool isFinal) { w->setChecked(true); }); }
/// <summary>
/// Uncheck all of the xform selection checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnXformsSelectNoneButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w, bool isFinal) { w->setChecked(false); }); }
/// <summary>
/// Return whether the checkbox at the specified index is checked.
/// </summary>
/// <param name="i">The index of the xform to check for selection</param>
/// <param name="checked">True if checked, else false.</param>
bool Fractorium::IsXformSelected(size_t i)
{
if (i < m_XformSelections.size())
if (const auto w = m_XformSelections[i])
return w->isChecked();
return false;
}
/// <summary>
/// Return the number of xforms selected, optionally counting the final xform.
/// </summary>
/// <param name="includeFinal">Whether to include the final in the count if selected.</param>
/// <returns>The caption string</returns>
int Fractorium::SelectedXformCount(bool includeFinal)
{
auto selCount = 0;
ForEachXformCheckbox([&](int index, QCheckBox * cb, bool isFinal)
{
if (cb->isChecked())
{
if (!isFinal || includeFinal)
selCount++;
}
});
return selCount;
}
/// <summary>
/// Clear all of the dynamically created xform checkboxes.
/// </summary>
void Fractorium::ClearXformsSelections()
{
QLayoutItem* child = nullptr;
m_XformSelections.clear();
m_XformsSelectionLayout->blockSignals(true);
while (m_XformsSelectionLayout->count() && (child = m_XformsSelectionLayout->takeAt(0)))
{
auto w = child->widget();
delete child;
delete w;
}
m_XformsSelectionLayout->blockSignals(false);
}
/// <summary>
/// Make a caption from an xform.
/// The caption will be the xform count + 1, optionally followed by the xform's name.
/// For final xforms, the string "Final" will be used in place of the count.
/// </summary>
/// <param name="i">The index of the xform to make a caption for</param>
/// <returns>The caption string</returns>
template <typename T>
QString FractoriumEmberController<T>::MakeXformCaption(size_t i)
{
const auto forceFinal = m_Fractorium->HaveFinal();
const auto isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(i, forceFinal);
QString caption = isFinal ? "Final" : QString::number(i + 1);
if (const auto xform = m_Ember.GetTotalXform(i, forceFinal))
if (!xform->m_Name.empty())
caption += " (" + QString::fromStdString(xform->m_Name) + ")";
return caption;
}
/// <summary>
/// Function to perform the specified operation on every dynamically created xform selection checkbox.
/// </summary>
/// <param name="func">The operation to perform which is a function taking the index of the xform, its checkbox, and a bool specifying whether it's final.</param>
void Fractorium::ForEachXformCheckbox(std::function<void(int, QCheckBox*, bool)> func)
{
int i = 0;
const auto haveFinal = HaveFinal();
for (auto& cb : m_XformSelections)
func(i++, cb, haveFinal && cb == m_XformSelections.back());
}
/// <summary>
/// Function to perform the specified operation on one dynamically created xform selection checkbox.
/// </summary>
/// <param name="i">The index of the checkbox</param>
/// <param name="func">The operation to perform</param>
/// <returns>True if the checkbox was found, else false.</returns>
template <typename T>
bool FractoriumEmberController<T>::XformCheckboxAt(int i, std::function<void(QCheckBox*)> func)
{
if (i < m_Fractorium->m_XformSelections.size())
{
if (const auto w = m_Fractorium->m_XformSelections[i])
{
func(w);
return true;
}
}
return false;
}
/// <summary>
/// Function to perform the specified operation on one dynamically created xform selection checkbox.
/// The checkbox is specified by the xform it corresponds to, rather than its index.
/// </summary>
/// <param name="xform">The xform that corresponds to the checkbox</param>
/// <param name="func">The operation to perform</param>
/// <returns>True if the checkbox was found, else false.</returns>
template <typename T>
bool FractoriumEmberController<T>::XformCheckboxAt(Xform<T>* xform, std::function<void(QCheckBox*)> func)
{
const auto forceFinal = m_Fractorium->HaveFinal();
return XformCheckboxAt(m_Ember.GetTotalXformIndex(xform, forceFinal), func);
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the xforms selection UI.
/// </summary>
void Fractorium::InitXformsSelectUI()
{
m_XformsSelectionLayout = dynamic_cast<QFormLayout*>(ui.XformsSelectGroupBoxScrollAreaWidget->layout());
connect(ui.XformsSelectAllButton, SIGNAL(clicked(bool)), this, SLOT(OnXformsSelectAllButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.XformsSelectNoneButton, SIGNAL(clicked(bool)), this, SLOT(OnXformsSelectNoneButtonClicked(bool)), Qt::QueuedConnection);
ClearXformsSelections();
}
/// <summary>
/// Check all of the xform selection checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnXformsSelectAllButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w, bool isFinal) { w->setChecked(true); }); }
/// <summary>
/// Uncheck all of the xform selection checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnXformsSelectNoneButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w, bool isFinal) { w->setChecked(false); }); }
/// <summary>
/// Return whether the checkbox at the specified index is checked.
/// </summary>
/// <param name="i">The index of the xform to check for selection</param>
/// <param name="checked">True if checked, else false.</param>
bool Fractorium::IsXformSelected(size_t i)
{
if (i < m_XformSelections.size())
if (const auto w = m_XformSelections[i])
return w->isChecked();
return false;
}
/// <summary>
/// Return the number of xforms selected, optionally counting the final xform.
/// </summary>
/// <param name="includeFinal">Whether to include the final in the count if selected.</param>
/// <returns>The caption string</returns>
int Fractorium::SelectedXformCount(bool includeFinal)
{
auto selCount = 0;
ForEachXformCheckbox([&](int index, QCheckBox * cb, bool isFinal)
{
if (cb->isChecked())
{
if (!isFinal || includeFinal)
selCount++;
}
});
return selCount;
}
/// <summary>
/// Clear all of the dynamically created xform checkboxes.
/// </summary>
void Fractorium::ClearXformsSelections()
{
QLayoutItem* child = nullptr;
m_XformSelections.clear();
m_XformsSelectionLayout->blockSignals(true);
while (m_XformsSelectionLayout->count() && (child = m_XformsSelectionLayout->takeAt(0)))
{
auto w = child->widget();
delete child;
delete w;
}
m_XformsSelectionLayout->blockSignals(false);
}
/// <summary>
/// Make a caption from an xform.
/// The caption will be the xform count + 1, optionally followed by the xform's name.
/// For final xforms, the string "Final" will be used in place of the count.
/// </summary>
/// <param name="i">The index of the xform to make a caption for</param>
/// <returns>The caption string</returns>
template <typename T>
QString FractoriumEmberController<T>::MakeXformCaption(size_t i)
{
const auto forceFinal = m_Fractorium->HaveFinal();
const auto isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(i, forceFinal);
QString caption = isFinal ? "Final" : QString::number(i + 1);
if (const auto xform = m_Ember.GetTotalXform(i, forceFinal))
if (!xform->m_Name.empty())
caption += " (" + QString::fromStdString(xform->m_Name) + ")";
return caption;
}
/// <summary>
/// Function to perform the specified operation on every dynamically created xform selection checkbox.
/// </summary>
/// <param name="func">The operation to perform which is a function taking the index of the xform, its checkbox, and a bool specifying whether it's final.</param>
void Fractorium::ForEachXformCheckbox(std::function<void(int, QCheckBox*, bool)> func)
{
int i = 0;
const auto haveFinal = HaveFinal();
for (auto& cb : m_XformSelections)
func(i++, cb, haveFinal && cb == m_XformSelections.back());
}
/// <summary>
/// Function to perform the specified operation on one dynamically created xform selection checkbox.
/// </summary>
/// <param name="i">The index of the checkbox</param>
/// <param name="func">The operation to perform</param>
/// <returns>True if the checkbox was found, else false.</returns>
template <typename T>
bool FractoriumEmberController<T>::XformCheckboxAt(int i, std::function<void(QCheckBox*)> func)
{
if (i < m_Fractorium->m_XformSelections.size())
{
if (const auto w = m_Fractorium->m_XformSelections[i])
{
func(w);
return true;
}
}
return false;
}
/// <summary>
/// Function to perform the specified operation on one dynamically created xform selection checkbox.
/// The checkbox is specified by the xform it corresponds to, rather than its index.
/// </summary>
/// <param name="xform">The xform that corresponds to the checkbox</param>
/// <param name="func">The operation to perform</param>
/// <returns>True if the checkbox was found, else false.</returns>
template <typename T>
bool FractoriumEmberController<T>::XformCheckboxAt(Xform<T>* xform, std::function<void(QCheckBox*)> func)
{
const auto forceFinal = m_Fractorium->HaveFinal();
return XformCheckboxAt(m_Ember.GetTotalXformIndex(xform, forceFinal), func);
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif

View File

@ -1,433 +1,433 @@
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the xforms variations UI.
/// </summary>
void Fractorium::InitXformsVariationsUI()
{
const auto 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.VariationsFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnVariationsFilterClearButtonClicked(bool)));
connect(ui.ActionVariationsDialog, SIGNAL(triggered(bool)), this, SLOT(OnActionVariationsDialog(bool)), Qt::QueuedConnection);
//Setting dimensions in the designer with a layout is futile, so must hard code here.
tree->setColumnWidth(0, 170);
tree->setColumnWidth(1, 80);
tree->setColumnWidth(2, 20);
//Set Default variation tree text and background colors for zero and non zero cases.
m_VariationTreeColorNonZero = Qt::black;
m_VariationTreeColorZero = Qt::black;
m_VariationTreeBgColorNonZero = Qt::lightGray;
m_VariationTreeBgColorZero = Qt::white;
}
/// <summary>
/// Show the variations filter dialog.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnActionVariationsDialog(bool checked)
{
if (m_VarDialog->exec())
{
m_Controller->FilteredVariations();
Filter();
}
}
/// <summary>
/// Apply the text passed in, in conjuction with the selections from
/// the variations filter dialog to only show variations whose names
/// contain the substring and are selected.
/// Called when the user types in the variation filter text box and
/// when the variations dialog exits.
/// </summary>
/// <param name="text">The text to filter on</param>
template <typename T>
void FractoriumEmberController<T>::Filter(const QString& text)
{
const auto& ids = m_Fractorium->m_VarDialog->Map();
const auto tree = m_Fractorium->ui.VariationsTree;
const auto xform = CurrentXform();
tree->setUpdatesEnabled(false);
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
if (auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i)))
{
auto varName = item->text(0);
if (xform && xform->GetVariationById(item->Id()))//If it's present then show it no matter what the filter is.
{
item->setHidden(false);
}
else if (ids.contains(varName))//If the varation is the map of all variations, which is should always be, consider it as well as the filter text.
{
item->setHidden(!varName.contains(text, Qt::CaseInsensitive) || !ids[varName].toBool());
}
else//Wasn't present, which should never happen, so just consider filter text.
{
item->setHidden(!varName.contains(text, Qt::CaseInsensitive));
}
}
}
m_Fractorium->OnTreeHeaderSectionClicked(m_Fractorium->m_VarSortMode);//Must re-sort every time the filter changes.
tree->setUpdatesEnabled(true);
}
void Fractorium::Filter()
{
m_Controller->Filter(ui.VariationsFilterLineEdit->text());
}
template <typename T>
void FractoriumEmberController<T>::FilteredVariations()
{
const auto& map = m_Fractorium->m_VarDialog->Map();
m_FilteredVariations.clear();
m_FilteredVariations.reserve(map.size());
for (auto i = 0; i < m_VariationList->Size(); i++)
if (const auto var = m_VariationList->GetVariation(i))
if (map.contains(var->Name().c_str()) && map[var->Name().c_str()].toBool())
m_FilteredVariations.push_back(var->VariationId());
}
/// <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>::SetupVariationsTree()
{
T fMin = TLOW;
T fMax = TMAX;
const QSize hint0(170, 16);
const QSize hint1(80, 16);
const QSize hint2(20, 16);
static vector<string> dc{ "m_ColorX" };
static vector<string> assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =",
"outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" };
auto tree = m_Fractorium->ui.VariationsTree;
tree->clear();
tree->blockSignals(true);
int iconSize_ = 20;
for (size_t i = 0; i < m_VariationList->Size(); i++)
{
auto var = m_VariationList->GetVariation(i);
auto parVar = dynamic_cast<const ParametricVariation<T>*>(var);
//First add the variation, with a spinner for its weight.
auto item = new VariationTreeWidgetItem(var->VariationId(), tree);
auto spinBox = new VariationTreeDoubleSpinBox(tree, item, var->VariationId(), "");
item->setText(0, QString::fromStdString(var->Name()));
item->setSizeHint(0, hint0);
item->setSizeHint(1, hint1);
item->setSizeHint(2, hint2);
QPixmap pixmap(iconSize_ * 3, iconSize_);
auto mask = pixmap.createMaskFromColor(QColor("transparent"), Qt::MaskOutColor);
pixmap.setMask(mask);
QPainter paint(&pixmap);
paint.fillRect(QRect(0, 0, iconSize_ * 3, iconSize_), QColor(0, 0, 0, 0));
if (var->VarType() == eVariationType::VARTYPE_REG)
{
if (SearchVar(var, assign, false))
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
}
else if (var->VarType() == eVariationType::VARTYPE_PRE || var->VarType() == eVariationType::VARTYPE_POST)
{
if (var->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
}
bool isDc = SearchVar(var, dc, false);
if (isDc)
paint.fillRect(QRect(iconSize_, 0, iconSize_, iconSize_), QColor(0, 255, 0));
if (!var->StateOpenCLString().empty())
paint.fillRect(QRect(iconSize_ * 2, 0, iconSize_, iconSize_), QColor(0, 0, 255));
QIcon qi(pixmap);
item->setIcon(2, qi);
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)
{
auto params = parVar->Params();
for (size_t j = 0; j < parVar->ParamCount(); j++)
{
if (!params[j].IsPrecalc())
{
auto paramWidget = new VariationTreeWidgetItem(var->VariationId(), item);
auto varSpinBox = new VariationTreeDoubleSpinBox(tree, paramWidget, parVar->VariationId(), 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() == eParamType::INTEGER || params[j].Type() == eParamType::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);
}
}
}
}
Filter("");
tree->blockSignals(false);
}
/// <summary>
/// Set every spinner in the variation tree, including params, to zero.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ClearVariationsTree()
{
const auto tree = m_Fractorium->ui.VariationsTree;
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
const auto item = tree->topLevelItem(i);
if (auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item, 1)))
{
spinBox->SetValueStealth(0);
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params.
{
if (const auto varSpinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item->child(j), 1)))//Cast the child widget to the VariationTreeDoubleSpinBox type.
varSpinBox->SetValueStealth(0);
}
}
}
}
/// <summary>
/// Copy the value of a variation or param spinner to its corresponding value
/// in the selected xforms.
/// 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)//Would be awesome to make this work for all.//TODO
{
bool update = false;
const auto objSender = m_Fractorium->sender();
const auto tree = m_Fractorium->ui.VariationsTree;
const auto sender = dynamic_cast<VariationTreeDoubleSpinBox*>(objSender);
if (sender)
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
{
const auto var = m_VariationList->GetVariation(sender->GetVariationId());//The variation attached to the sender, for reference only.
const auto parVar = dynamic_cast<const ParametricVariation<T>*>(var);//The parametric cast of that variation.
const auto xformVar = xform->GetVariationById(var->VariationId());//The corresponding variation in the currently selected xform.
const auto widgetItem = sender->WidgetItem();
const auto 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 (const auto xformParVar = dynamic_cast<ParametricVariation<T>*>(xformVar))//The parametric cast of the xform's variation.
if (xformParVar->SetParamVal(sender->ParamName().c_str(), d))
update = true;
}
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());
widgetItem->setTextColor(0, m_Fractorium->m_VariationTreeColorZero);
widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorZero);
}
else
{
if (xformVar)//The xform already contained this variation, which means they just went from a non-zero weight to another non-zero weight (the simple case).
{
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.
const auto newVar = var->Copy();//Create a new one with default values.
newVar->m_Weight = d;
xform->AddVariation(newVar);
widgetItem->setTextColor(0, m_Fractorium->m_VariationTreeColorNonZero);
widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorNonZero);
//If they've added a new parametric variation, then grab the values currently in the spinners
//for the child parameters and assign them to the newly added variation.
if (parVar)
{
const auto newParVar = dynamic_cast<ParametricVariation<T>*>(newVar);
for (int i = 0; i < widgetItem->childCount(); i++)//Iterate through all of the children, which will be the params.
{
const auto childItem = widgetItem->child(i);//Get the child.
const auto itemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
if (const auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(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());
}
}
}
}
}
update = true;
}
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);
if (update)
UpdateRender();
}
}
void Fractorium::OnVariationSpinBoxValueChanged(double d) { m_Controller->VariationSpinBoxValueChanged(d); }
/// <summary>
/// Fill in the variations tree with the values from the current xform.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillVariationTreeWithCurrentXform()
{
FillVariationTreeWithXform(CurrentXform());
}
/// <summary>
/// Fill the variation tree values from passed in xform and apply the current sorting mode.
/// Called when the currently selected xform changes.
/// </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)
{
const auto tree = m_Fractorium->ui.VariationsTree;
tree->blockSignals(true);
m_Fractorium->Filter();
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
const auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i));
const auto var = xform->GetVariationById(item->Id());//See if this variation in the tree was contained in the xform.
const auto parVar = dynamic_cast<ParametricVariation<T>*>(var);//Attempt cast to parametric variation for later.
const auto origParVar = dynamic_cast<const ParametricVariation<T>*>(m_VariationList->GetVariation(item->Id()));
if (const auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item, 1)))//Get the widget for the item, and cast the widget to the VariationTreeDoubleSpinBox type.
{
if (var)//Ensure it's visible, even if it's supposed to be filtered.
item->setHidden(false);
spinBox->SetValueStealth(var ? var->m_Weight : 0);//If the variation was present, set the spin box to its weight, else zero.
item->setTextColor(0, var ? m_Fractorium->m_VariationTreeColorNonZero : m_Fractorium->m_VariationTreeColorZero);
item->setBackgroundColor(0, var ? m_Fractorium->m_VariationTreeBgColorNonZero : m_Fractorium->m_VariationTreeBgColorZero);
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation.
{
T* param = nullptr;
const auto childItem = item->child(j);//Get the child.
const auto childItemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
if (const auto childSpinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(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.
}
}
}
}
}
tree->blockSignals(false);
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 alternate 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)
{
if (logicalIndex <= 1)
{
m_VarSortMode = logicalIndex;
ui.VariationsTree->sortItems(m_VarSortMode, m_VarSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
if (m_VarSortMode == 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)
{
Filter();
}
/// <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();
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif
#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the xforms variations UI.
/// </summary>
void Fractorium::InitXformsVariationsUI()
{
const auto 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.VariationsFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnVariationsFilterClearButtonClicked(bool)));
connect(ui.ActionVariationsDialog, SIGNAL(triggered(bool)), this, SLOT(OnActionVariationsDialog(bool)), Qt::QueuedConnection);
//Setting dimensions in the designer with a layout is futile, so must hard code here.
tree->setColumnWidth(0, 170);
tree->setColumnWidth(1, 80);
tree->setColumnWidth(2, 20);
//Set Default variation tree text and background colors for zero and non zero cases.
m_VariationTreeColorNonZero = Qt::black;
m_VariationTreeColorZero = Qt::black;
m_VariationTreeBgColorNonZero = Qt::lightGray;
m_VariationTreeBgColorZero = Qt::white;
}
/// <summary>
/// Show the variations filter dialog.
/// </summary>
/// <param name="checked">Ignored</param>
void Fractorium::OnActionVariationsDialog(bool checked)
{
if (m_VarDialog->exec())
{
m_Controller->FilteredVariations();
Filter();
}
}
/// <summary>
/// Apply the text passed in, in conjuction with the selections from
/// the variations filter dialog to only show variations whose names
/// contain the substring and are selected.
/// Called when the user types in the variation filter text box and
/// when the variations dialog exits.
/// </summary>
/// <param name="text">The text to filter on</param>
template <typename T>
void FractoriumEmberController<T>::Filter(const QString& text)
{
const auto& ids = m_Fractorium->m_VarDialog->Map();
const auto tree = m_Fractorium->ui.VariationsTree;
const auto xform = CurrentXform();
tree->setUpdatesEnabled(false);
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
if (auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i)))
{
auto varName = item->text(0);
if (xform && xform->GetVariationById(item->Id()))//If it's present then show it no matter what the filter is.
{
item->setHidden(false);
}
else if (ids.contains(varName))//If the varation is the map of all variations, which is should always be, consider it as well as the filter text.
{
item->setHidden(!varName.contains(text, Qt::CaseInsensitive) || !ids[varName].toBool());
}
else//Wasn't present, which should never happen, so just consider filter text.
{
item->setHidden(!varName.contains(text, Qt::CaseInsensitive));
}
}
}
m_Fractorium->OnTreeHeaderSectionClicked(m_Fractorium->m_VarSortMode);//Must re-sort every time the filter changes.
tree->setUpdatesEnabled(true);
}
void Fractorium::Filter()
{
m_Controller->Filter(ui.VariationsFilterLineEdit->text());
}
template <typename T>
void FractoriumEmberController<T>::FilteredVariations()
{
const auto& map = m_Fractorium->m_VarDialog->Map();
m_FilteredVariations.clear();
m_FilteredVariations.reserve(map.size());
for (auto i = 0; i < m_VariationList->Size(); i++)
if (const auto var = m_VariationList->GetVariation(i))
if (map.contains(var->Name().c_str()) && map[var->Name().c_str()].toBool())
m_FilteredVariations.push_back(var->VariationId());
}
/// <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>::SetupVariationsTree()
{
T fMin = TLOW;
T fMax = TMAX;
const QSize hint0(170, 16);
const QSize hint1(80, 16);
const QSize hint2(20, 16);
static vector<string> dc{ "m_ColorX" };
static vector<string> assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =",
"outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" };
auto tree = m_Fractorium->ui.VariationsTree;
tree->clear();
tree->blockSignals(true);
int iconSize_ = 20;
for (size_t i = 0; i < m_VariationList->Size(); i++)
{
auto var = m_VariationList->GetVariation(i);
auto parVar = dynamic_cast<const ParametricVariation<T>*>(var);
//First add the variation, with a spinner for its weight.
auto item = new VariationTreeWidgetItem(var->VariationId(), tree);
auto spinBox = new VariationTreeDoubleSpinBox(tree, item, var->VariationId(), "");
item->setText(0, QString::fromStdString(var->Name()));
item->setSizeHint(0, hint0);
item->setSizeHint(1, hint1);
item->setSizeHint(2, hint2);
QPixmap pixmap(iconSize_ * 3, iconSize_);
auto mask = pixmap.createMaskFromColor(QColor("transparent"), Qt::MaskOutColor);
pixmap.setMask(mask);
QPainter paint(&pixmap);
paint.fillRect(QRect(0, 0, iconSize_ * 3, iconSize_), QColor(0, 0, 0, 0));
if (var->VarType() == eVariationType::VARTYPE_REG)
{
if (SearchVar(var, assign, false))
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
}
else if (var->VarType() == eVariationType::VARTYPE_PRE || var->VarType() == eVariationType::VARTYPE_POST)
{
if (var->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
}
bool isDc = SearchVar(var, dc, false);
if (isDc)
paint.fillRect(QRect(iconSize_, 0, iconSize_, iconSize_), QColor(0, 255, 0));
if (!var->StateOpenCLString().empty())
paint.fillRect(QRect(iconSize_ * 2, 0, iconSize_, iconSize_), QColor(0, 0, 255));
QIcon qi(pixmap);
item->setIcon(2, qi);
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)
{
auto params = parVar->Params();
for (size_t j = 0; j < parVar->ParamCount(); j++)
{
if (!params[j].IsPrecalc())
{
auto paramWidget = new VariationTreeWidgetItem(var->VariationId(), item);
auto varSpinBox = new VariationTreeDoubleSpinBox(tree, paramWidget, parVar->VariationId(), 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() == eParamType::INTEGER || params[j].Type() == eParamType::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);
}
}
}
}
Filter("");
tree->blockSignals(false);
}
/// <summary>
/// Set every spinner in the variation tree, including params, to zero.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::ClearVariationsTree()
{
const auto tree = m_Fractorium->ui.VariationsTree;
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
const auto item = tree->topLevelItem(i);
if (auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item, 1)))
{
spinBox->SetValueStealth(0);
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params.
{
if (const auto varSpinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item->child(j), 1)))//Cast the child widget to the VariationTreeDoubleSpinBox type.
varSpinBox->SetValueStealth(0);
}
}
}
}
/// <summary>
/// Copy the value of a variation or param spinner to its corresponding value
/// in the selected xforms.
/// 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)//Would be awesome to make this work for all.//TODO
{
bool update = false;
const auto objSender = m_Fractorium->sender();
const auto tree = m_Fractorium->ui.VariationsTree;
const auto sender = dynamic_cast<VariationTreeDoubleSpinBox*>(objSender);
if (sender)
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
{
const auto var = m_VariationList->GetVariation(sender->GetVariationId());//The variation attached to the sender, for reference only.
const auto parVar = dynamic_cast<const ParametricVariation<T>*>(var);//The parametric cast of that variation.
const auto xformVar = xform->GetVariationById(var->VariationId());//The corresponding variation in the currently selected xform.
const auto widgetItem = sender->WidgetItem();
const auto 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 (const auto xformParVar = dynamic_cast<ParametricVariation<T>*>(xformVar))//The parametric cast of the xform's variation.
if (xformParVar->SetParamVal(sender->ParamName().c_str(), d))
update = true;
}
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());
widgetItem->setForeground(0, m_Fractorium->m_VariationTreeColorZero);
widgetItem->setBackground(0, m_Fractorium->m_VariationTreeBgColorZero);
}
else
{
if (xformVar)//The xform already contained this variation, which means they just went from a non-zero weight to another non-zero weight (the simple case).
{
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.
const auto newVar = var->Copy();//Create a new one with default values.
newVar->m_Weight = d;
xform->AddVariation(newVar);
widgetItem->setForeground(0, m_Fractorium->m_VariationTreeColorNonZero);
widgetItem->setBackground(0, m_Fractorium->m_VariationTreeBgColorNonZero);
//If they've added a new parametric variation, then grab the values currently in the spinners
//for the child parameters and assign them to the newly added variation.
if (parVar)
{
const auto newParVar = dynamic_cast<ParametricVariation<T>*>(newVar);
for (int i = 0; i < widgetItem->childCount(); i++)//Iterate through all of the children, which will be the params.
{
const auto childItem = widgetItem->child(i);//Get the child.
const auto itemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
if (const auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(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());
}
}
}
}
}
update = true;
}
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);
if (update)
UpdateRender();
}
}
void Fractorium::OnVariationSpinBoxValueChanged(double d) { m_Controller->VariationSpinBoxValueChanged(d); }
/// <summary>
/// Fill in the variations tree with the values from the current xform.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillVariationTreeWithCurrentXform()
{
FillVariationTreeWithXform(CurrentXform());
}
/// <summary>
/// Fill the variation tree values from passed in xform and apply the current sorting mode.
/// Called when the currently selected xform changes.
/// </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)
{
const auto tree = m_Fractorium->ui.VariationsTree;
tree->blockSignals(true);
m_Fractorium->Filter();
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
const auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i));
const auto var = xform->GetVariationById(item->Id());//See if this variation in the tree was contained in the xform.
const auto parVar = dynamic_cast<ParametricVariation<T>*>(var);//Attempt cast to parametric variation for later.
const auto origParVar = dynamic_cast<const ParametricVariation<T>*>(m_VariationList->GetVariation(item->Id()));
if (const auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item, 1)))//Get the widget for the item, and cast the widget to the VariationTreeDoubleSpinBox type.
{
if (var)//Ensure it's visible, even if it's supposed to be filtered.
item->setHidden(false);
spinBox->SetValueStealth(var ? var->m_Weight : 0);//If the variation was present, set the spin box to its weight, else zero.
item->setForeground(0, var ? m_Fractorium->m_VariationTreeColorNonZero : m_Fractorium->m_VariationTreeColorZero);
item->setBackground(0, var ? m_Fractorium->m_VariationTreeBgColorNonZero : m_Fractorium->m_VariationTreeBgColorZero);
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation.
{
T* param = nullptr;
const auto childItem = item->child(j);//Get the child.
const auto childItemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
if (const auto childSpinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(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.
}
}
}
}
}
tree->blockSignals(false);
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 alternate 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)
{
if (logicalIndex <= 1)
{
m_VarSortMode = logicalIndex;
ui.VariationsTree->sortItems(m_VarSortMode, m_VarSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
if (m_VarSortMode == 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)
{
Filter();
}
/// <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();
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif

View File

@ -1,306 +1,306 @@
#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 = eAffineType::AffinePre;
m_HoverType = eHoverType::HoverNone;
m_DragState = eDragState::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 = static_cast<T>(1.0 / 4.0); // michel, needs to insert on GUI to be flexible//TODO
m_FractoriumEmberController = controller;
m_HoverXform = nullptr;
m_SelectedXform = nullptr;
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>
/// Reset the drag and hover state. Called in response setting a new ember as the current one.
/// </summary>
template <typename T>
void GLEmberController<T>::ResetMouseState()
{
m_HoverType = eHoverType::HoverNone;
m_HoverXform = nullptr;
m_SelectedXform = nullptr;
}
/// <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.
const auto windowCenter = ScrolledCenter(false);
const v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y);
const v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y);
const T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter);
const 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()
{
const auto scrolledWorldCenter = ScrolledCenter(true);
const T rotStart = NormalizeDeg180<T>((std::atan2(m_MouseDownWorldPos.y - scrolledWorldCenter.y, m_MouseDownWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T));
const T rot = NormalizeDeg180<T>((std::atan2(m_MouseWorldPos.y - scrolledWorldCenter.y, m_MouseWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T));
return rotStart - rot;
}
/// <summary>
/// Return the window coordinates of the center of the viewable area.
/// This is the middle of the parent scroll area plus the scroll bar offset, all scaled by the device pixel ratio.
/// </summary>
/// <param name="toWorld">True to return world coordinates, else return window coordinates.</param>
/// <returns>The coordinates of the center of the viewable area in either window space or world space.</returns>
template <typename T>
v3T GLEmberController<T>::ScrolledCenter(bool toWorld)
{
const auto dprf = m_GL->devicePixelRatioF();
const auto wpsa = m_Fractorium->ui.GLParentScrollArea->width();
const auto hpsa = m_Fractorium->ui.GLParentScrollArea->height();
const auto hpos = m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->value();
const auto vpos = m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->value();
v3T v;
if (!m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->isVisible() && !m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->isVisible())
v = v3T(((m_GL->width() / 2)) * dprf,
((m_GL->height() / 2)) * dprf,
0);
else
v = v3T((hpos + (wpsa / 2)) * dprf,
(vpos + (hpsa / 2)) * dprf,
0);
if (toWorld)
return WindowToWorld(v, true);
return v;
}
/// <summary>
/// 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 v2T GLEmberController<T>::SnapToGrid(const v2T& vec) const
{
return v2T(glm::round(vec.x / GridStep) * GridStep, glm::round(vec.y / GridStep) * GridStep);
}
/// <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(const v3T& vec) const
{
return v3T(glm::round(vec.x / GridStep) * GridStep,
glm::round(vec.y / GridStep) * GridStep,
vec.z);
}
/// <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(const v3T& vec, uint divisions) const
{
T bestRsq = numeric_limits<T>::max();
v3T c(0, 0, 0), best;
best.x = 1;
best.y = 0;
for (uint i = 0; i < divisions; i++)
{
const auto theta = 2.0 * M_PI * static_cast<T>(i) / static_cast<T>(divisions);
c.x = std::cos(theta);
c.y = std::sin(theta);
const auto 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(const v3T& v, bool flip) const
{
const 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()
{
#ifndef USE_GLSL
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));
#else
m_Viewport = m_GL->m_Viewport;
glm::tmat4x4<float, glm::defaultp> tempmat = glm::make_mat4(m_GL->m_ModelViewMatrix.data());
m_Modelview = tempmat;
tempmat = glm::make_mat4(m_GL->m_ProjMatrix.data());
m_Projection = tempmat;
#endif
}
#ifdef DO_DOUBLE
/// <summary>
/// Template specialization for querying the viewport, modelview and projection
/// matrices as doubles.
/// </summary>
template <>
void GLEmberController<double>::QueryVMP()
{
#ifndef USE_GLSL
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));
#else
m_Viewport = m_GL->m_Viewport;
glm::tmat4x4<float, glm::defaultp> tempmat = glm::make_mat4(m_GL->m_ModelViewMatrix.data());
m_Modelview = tempmat;
tempmat = glm::make_mat4(m_GL->m_ProjMatrix.data());
m_Projection = tempmat;
#endif
}
#endif
/// <summary>
/// Template specialization for multiplying the current matrix
/// by an m4<float>.
/// </summary>
#ifndef USE_GLSL
template <>
void GLEmberController<float>::MultMatrix(tmat4x4<float, glm::defaultp>& mat)
{
m_GL->glMultMatrixf(glm::value_ptr(mat));
}
#endif
#ifdef DO_DOUBLE
/// <summary>
/// Template specialization for multiplying the current matrix
/// by an m4<double>.
/// </summary>
#ifndef USE_GLSL
template <>
void GLEmberController<double>::MultMatrix(tmat4x4<double, glm::defaultp>& mat)
{
m_GL->glMultMatrixd(glm::value_ptr(mat));
}
#endif
#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)
{
if (const auto renderer = m_FractoriumEmberController->Renderer())
{
QueryVMP();
if (print)
{
for (glm::length_t i = 0; i < 4; i++)
qDebug() << "Viewport[" << i << "] = " << m_Viewport[i] << "\n";
for (glm::length_t i = 0; i < 16; i++)
qDebug() << "Modelview[" << i << "] = " << glm::value_ptr(m_Modelview)[i] << "\n";
for (glm::length_t i = 0; i < 16; i++)
qDebug() << "Projection[" << i << "] = " << glm::value_ptr(m_Projection)[i] << "\n";
}
}
}
template class GLEmberController<float>;
#ifdef DO_DOUBLE
template class GLEmberController<double>;
#endif
#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 = eAffineType::AffinePre;
m_HoverType = eHoverType::HoverNone;
m_DragState = eDragState::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 = static_cast<T>(1.0 / 4.0); // michel, needs to insert on GUI to be flexible//TODO
m_FractoriumEmberController = controller;
m_HoverXform = nullptr;
m_SelectedXform = nullptr;
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>
/// Reset the drag and hover state. Called in response setting a new ember as the current one.
/// </summary>
template <typename T>
void GLEmberController<T>::ResetMouseState()
{
m_HoverType = eHoverType::HoverNone;
m_HoverXform = nullptr;
m_SelectedXform = nullptr;
}
/// <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.
const auto windowCenter = ScrolledCenter(false);
const v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y);
const v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y);
const T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter);
const 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()
{
const auto scrolledWorldCenter = ScrolledCenter(true);
const T rotStart = NormalizeDeg180<T>((std::atan2(m_MouseDownWorldPos.y - scrolledWorldCenter.y, m_MouseDownWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T));
const T rot = NormalizeDeg180<T>((std::atan2(m_MouseWorldPos.y - scrolledWorldCenter.y, m_MouseWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T));
return rotStart - rot;
}
/// <summary>
/// Return the window coordinates of the center of the viewable area.
/// This is the middle of the parent scroll area plus the scroll bar offset, all scaled by the device pixel ratio.
/// </summary>
/// <param name="toWorld">True to return world coordinates, else return window coordinates.</param>
/// <returns>The coordinates of the center of the viewable area in either window space or world space.</returns>
template <typename T>
v3T GLEmberController<T>::ScrolledCenter(bool toWorld)
{
const auto dprf = m_GL->devicePixelRatioF();
const auto wpsa = m_Fractorium->ui.GLParentScrollArea->width();
const auto hpsa = m_Fractorium->ui.GLParentScrollArea->height();
const auto hpos = m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->value();
const auto vpos = m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->value();
v3T v;
if (!m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->isVisible() && !m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->isVisible())
v = v3T(((m_GL->width() / 2)) * dprf,
((m_GL->height() / 2)) * dprf,
0);
else
v = v3T((hpos + (wpsa / 2)) * dprf,
(vpos + (hpsa / 2)) * dprf,
0);
if (toWorld)
return WindowToWorld(v, true);
return v;
}
/// <summary>
/// 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 v2T GLEmberController<T>::SnapToGrid(const v2T& vec) const
{
return v2T(glm::round(vec.x / GridStep) * GridStep, glm::round(vec.y / GridStep) * GridStep);
}
/// <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(const v3T& vec) const
{
return v3T(glm::round(vec.x / GridStep) * GridStep,
glm::round(vec.y / GridStep) * GridStep,
vec.z);
}
/// <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(const v3T& vec, uint divisions) const
{
T bestRsq = numeric_limits<T>::max();
v3T c(0, 0, 0), best;
best.x = 1;
best.y = 0;
for (uint i = 0; i < divisions; i++)
{
const auto theta = 2.0 * M_PI * static_cast<T>(i) / static_cast<T>(divisions);
c.x = std::cos(theta);
c.y = std::sin(theta);
const auto 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(const v3T& v, bool flip) const
{
const 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()
{
#ifndef USE_GLSL
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));
#else
m_Viewport = m_GL->m_Viewport;
glm::tmat4x4<float, glm::defaultp> tempmat = glm::make_mat4(m_GL->m_ModelViewMatrix.data());
m_Modelview = tempmat;
tempmat = glm::make_mat4(m_GL->m_ProjMatrix.data());
m_Projection = tempmat;
#endif
}
#ifdef DO_DOUBLE
/// <summary>
/// Template specialization for querying the viewport, modelview and projection
/// matrices as doubles.
/// </summary>
template <>
void GLEmberController<double>::QueryVMP()
{
#ifndef USE_GLSL
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));
#else
m_Viewport = m_GL->m_Viewport;
glm::tmat4x4<float, glm::defaultp> tempmat = glm::make_mat4(m_GL->m_ModelViewMatrix.data());
m_Modelview = tempmat;
tempmat = glm::make_mat4(m_GL->m_ProjMatrix.data());
m_Projection = tempmat;
#endif
}
#endif
/// <summary>
/// Template specialization for multiplying the current matrix
/// by an m4<float>.
/// </summary>
#ifndef USE_GLSL
template <>
void GLEmberController<float>::MultMatrix(tmat4x4<float, glm::defaultp>& mat)
{
m_GL->glMultMatrixf(glm::value_ptr(mat));
}
#endif
#ifdef DO_DOUBLE
/// <summary>
/// Template specialization for multiplying the current matrix
/// by an m4<double>.
/// </summary>
#ifndef USE_GLSL
template <>
void GLEmberController<double>::MultMatrix(tmat4x4<double, glm::defaultp>& mat)
{
m_GL->glMultMatrixd(glm::value_ptr(mat));
}
#endif
#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)
{
if (const auto renderer = m_FractoriumEmberController->Renderer())
{
QueryVMP();
if (print)
{
for (glm::length_t i = 0; i < 4; i++)
qDebug() << "Viewport[" << i << "] = " << m_Viewport[i] << "\n";
for (glm::length_t i = 0; i < 16; i++)
qDebug() << "Modelview[" << i << "] = " << glm::value_ptr(m_Modelview)[i] << "\n";
for (glm::length_t i = 0; i < 16; i++)
qDebug() << "Projection[" << i << "] = " << glm::value_ptr(m_Projection)[i] << "\n";
}
}
}
template class GLEmberController<float>;
#ifdef DO_DOUBLE
template class GLEmberController<double>;
#endif

View File

@ -1,162 +1,162 @@
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// GLEmberControllerBase and GLEmberController<T> classes.
/// </summary>
/// <summary>
/// Use/draw pre or post affine transform.
/// </summary>
enum class eAffineType : et { AffinePre, AffinePost };
/// <summary>
/// Hovering over nothing, the x axis, the y axis or the center.
/// </summary>
enum class eHoverType : et { HoverNone, HoverXAxis, HoverYAxis, HoverTranslation };
/// <summary>
/// Dragging an affine transform or panning, rotating or scaling the image.
/// </summary>
enum class eDragState : et { DragNone, DragSelect, DragPanning, DragDragging, DragRotateScale, DragPitchYaw };
/// <summary>
/// Dragging with no keys pressed, shift, control or alt.
/// </summary>
enum class eDragModifier : et { 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;
#define USE_GLSL 1
/// <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);
bool GetAlt();
bool GetShift();
bool GetControl();
void SetAlt();
void SetShift();
void SetControl();
void ClearAlt();
void ClearShift();
void ClearControl();
eDragState DragState() { return m_DragState; }
eAffineType AffineType() { return m_AffineType; }
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 SizesMatch() { return false; }
virtual bool CheckForSizeMismatch(int w, int h) { return true; }
virtual void QueryMatrices(bool print) { }
virtual void ResetMouseState() { }
protected:
uint 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();
void DrawImage() override;
void DrawAffines(bool pre, bool post) override;
void ClearWindow() override;
void MousePress(QMouseEvent* e) override;
void MouseRelease(QMouseEvent* e) override;
void MouseMove(QMouseEvent* e) override;
void Wheel(QWheelEvent* e) override;
void QueryMatrices(bool print) override;
bool SizesMatch() override;
bool CheckForSizeMismatch(int w, int h) override;
void ResetMouseState() override;
T CalcScale();
T CalcRotation();
v3T ScrolledCenter(bool toWorld = false);
void CalcDragXAxis();
void CalcDragYAxis();
void CalcDragTranslation();
void SetSelectedXform(Xform<T>* xform);
void DrawGrid();
void DrawAffine(const Xform<T>* xform, bool pre, bool selected, bool hovered);
int UpdateHover(const v3T& glCoords);
bool CheckXformHover(const Xform<T>* xform, const v3T& glCoords, T& bestDist, bool pre, bool post);
private:
v2T SnapToGrid(const v2T& vec) const;
v3T SnapToGrid(const v3T& vec) const;
v3T SnapToNormalizedAngle(const v3T& vec, uint divisions) const;
v3T WindowToWorld(const v3T& v, bool flip) const;
void QueryVMP();
#ifndef USE_GLSL
void MultMatrix(m4T& mat);
#endif
T m_CenterDownX;
T m_CenterDownY;
T m_RotationDown;
T m_ScaleDown;
T m_PitchDown;
T m_YawDown;
v4T m_BoundsDown;
v3T m_MouseWorldPos;
v3T m_MouseDownWorldPos;
v3T m_DragHandlePos;
v3T m_HoverHandlePos;
m4T m_Modelview;
m4T m_Projection;
Affine2D<T> m_DragSrcTransform;
std::map<size_t, Affine2D<T>> m_DragSrcPreTransforms;
std::map<size_t, Affine2D<T>> m_DragSrcPostTransforms;
Xform<T>* m_HoverXform;
Xform<T>* m_SelectedXform;
FractoriumEmberController<T>* m_FractoriumEmberController;
T GridStep;
vector<float> m_Verts;//Can't make this T because GLSL only works with floats.
};
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// GLEmberControllerBase and GLEmberController<T> classes.
/// </summary>
/// <summary>
/// Use/draw pre or post affine transform.
/// </summary>
enum class eAffineType : et { AffinePre, AffinePost };
/// <summary>
/// Hovering over nothing, the x axis, the y axis or the center.
/// </summary>
enum class eHoverType : et { HoverNone, HoverXAxis, HoverYAxis, HoverTranslation };
/// <summary>
/// Dragging an affine transform or panning, rotating or scaling the image.
/// </summary>
enum class eDragState : et { DragNone, DragSelect, DragPanning, DragDragging, DragRotateScale, DragPitchYaw };
/// <summary>
/// Dragging with no keys pressed, shift, control or alt.
/// </summary>
enum class eDragModifier : et { 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;
#define USE_GLSL 1
/// <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);
bool GetAlt();
bool GetShift();
bool GetControl();
void SetAlt();
void SetShift();
void SetControl();
void ClearAlt();
void ClearShift();
void ClearControl();
eDragState DragState() { return m_DragState; }
eAffineType AffineType() { return m_AffineType; }
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 SizesMatch() { return false; }
virtual bool CheckForSizeMismatch(int w, int h) { return true; }
virtual void QueryMatrices(bool print) { }
virtual void ResetMouseState() { }
protected:
uint 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();
void DrawImage() override;
void DrawAffines(bool pre, bool post) override;
void ClearWindow() override;
void MousePress(QMouseEvent* e) override;
void MouseRelease(QMouseEvent* e) override;
void MouseMove(QMouseEvent* e) override;
void Wheel(QWheelEvent* e) override;
void QueryMatrices(bool print) override;
bool SizesMatch() override;
bool CheckForSizeMismatch(int w, int h) override;
void ResetMouseState() override;
T CalcScale();
T CalcRotation();
v3T ScrolledCenter(bool toWorld = false);
void CalcDragXAxis();
void CalcDragYAxis();
void CalcDragTranslation();
void SetSelectedXform(Xform<T>* xform);
void DrawGrid();
void DrawAffine(const Xform<T>* xform, bool pre, bool selected, bool hovered);
int UpdateHover(const v3T& glCoords);
bool CheckXformHover(const Xform<T>* xform, const v3T& glCoords, T& bestDist, bool pre, bool post);
private:
v2T SnapToGrid(const v2T& vec) const;
v3T SnapToGrid(const v3T& vec) const;
v3T SnapToNormalizedAngle(const v3T& vec, uint divisions) const;
v3T WindowToWorld(const v3T& v, bool flip) const;
void QueryVMP();
#ifndef USE_GLSL
void MultMatrix(m4T& mat);
#endif
T m_CenterDownX;
T m_CenterDownY;
T m_RotationDown;
T m_ScaleDown;
T m_PitchDown;
T m_YawDown;
v4T m_BoundsDown;
v3T m_MouseWorldPos;
v3T m_MouseDownWorldPos;
v3T m_DragHandlePos;
v3T m_HoverHandlePos;
m4T m_Modelview;
m4T m_Projection;
Affine2D<T> m_DragSrcTransform;
std::map<size_t, Affine2D<T>> m_DragSrcPreTransforms;
std::map<size_t, Affine2D<T>> m_DragSrcPostTransforms;
Xform<T>* m_HoverXform;
Xform<T>* m_SelectedXform;
FractoriumEmberController<T>* m_FractoriumEmberController;
T GridStep;
vector<float> m_Verts;//Can't make this T because GLSL only works with floats.
};

File diff suppressed because it is too large Load Diff

View File

@ -1,117 +1,117 @@
#pragma once
#include "FractoriumEmberController.h"
/// <summary>
/// GLWidget class.
/// </summary>
class Fractorium;//Forward declaration since Fractorium uses this widget.
class GLEmberControllerBase;
template<typename T> class GLEmberController;
template<typename T> class FractoriumEmberController;
/// <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 QOpenGLWidget, protected
#ifdef USE_GLSL
QOpenGLFunctions
#else
QOpenGLFunctions_2_0
#endif
{
Q_OBJECT
friend Fractorium;
friend FractoriumEmberController<float>;
friend GLEmberControllerBase;
friend GLEmberController<float>;
#ifdef DO_DOUBLE
friend GLEmberController<double>;
friend FractoriumEmberController<double>;
#endif
public:
GLWidget(QWidget* p = nullptr);
~GLWidget();
void InitGL();
void DrawQuad();
void SetMainWindow(Fractorium* f);
bool Init();
bool Drawing();
GLint MaxTexSize();
GLuint OutputTexID();
GLint TexWidth();
GLint TexHeight();
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
virtual void keyPressEvent(QKeyEvent* e) override;
virtual void keyReleaseEvent(QKeyEvent* e) override;
virtual void mousePressEvent(QMouseEvent* e) override;
virtual void mouseReleaseEvent(QMouseEvent* e) override;
virtual void mouseMoveEvent(QMouseEvent* e) override;
virtual void wheelEvent(QWheelEvent* e) override;
void DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, bool dashed = false, GLfloat pointSize = 1.0f);
void DrawPointOrLine(const QVector4D& col, const std::vector<float>& vertices, int drawType, bool dashed = false, GLfloat pointSize = 1.0f);
private:
void SetDimensions(int w, int h);
bool Allocate(bool force = false);
bool Deallocate();
void SetViewport();
void DrawUnitSquare();
void DrawAffineHelper(int index, float circleWidth, float lineWidth, bool selected, bool hovered, bool pre, bool final, bool background);
GLEmberControllerBase* GLController();
bool m_Init = false;
bool m_Drawing = false;
GLint m_MaxTexSize = 16384;
GLint m_TexWidth = 0;
GLint m_TexHeight = 0;
GLint m_ViewWidth = 0;
GLint m_ViewHeight = 0;
GLuint m_OutputTexID = 0;
#ifdef USE_GLSL
GLuint m_PosAttr;
GLuint m_ColAttr;
GLuint m_PointSizeUniform;
GLuint m_MatrixUniform;
GLuint m_TexturePosAttr;
GLuint m_TextureUniform;
GLuint m_TextureMatrixUniform;
glm::ivec4 m_Viewport;
QMatrix4x4 m_ProjMatrix;
QMatrix4x4 m_ModelViewMatrix;
QMatrix4x4 m_ModelViewProjectionMatrix;
QMatrix4x4 m_TextureProjMatrix;
vector<float> m_Verts;
std::array<GLfloat, 10> m_TexVerts = std::array<GLfloat, 10>
{
0, 0,
0, 1,
1, 1,
1, 0,
0, 0
};
QOpenGLShaderProgram* m_Program = nullptr;
QOpenGLShaderProgram* m_QuadProgram = nullptr;
#endif
Fractorium* m_Fractorium = nullptr;
};
#pragma once
#include "FractoriumEmberController.h"
/// <summary>
/// GLWidget class.
/// </summary>
class Fractorium;//Forward declaration since Fractorium uses this widget.
class GLEmberControllerBase;
template<typename T> class GLEmberController;
template<typename T> class FractoriumEmberController;
/// <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 QOpenGLWidget, protected
#ifdef USE_GLSL
QOpenGLFunctions
#else
QOpenGLFunctions_2_0
#endif
{
Q_OBJECT
friend Fractorium;
friend FractoriumEmberController<float>;
friend GLEmberControllerBase;
friend GLEmberController<float>;
#ifdef DO_DOUBLE
friend GLEmberController<double>;
friend FractoriumEmberController<double>;
#endif
public:
GLWidget(QWidget* p = nullptr);
~GLWidget();
void InitGL();
void DrawQuad();
void SetMainWindow(Fractorium* f);
bool Init() const;
bool Drawing() const;
GLint MaxTexSize() const;
GLuint OutputTexID() const;
GLint TexWidth() const;
GLint TexHeight() const;
protected:
void initializeGL() override;
void paintGL() override;
void keyPressEvent(QKeyEvent* e) override;
void keyReleaseEvent(QKeyEvent* e) override;
void mousePressEvent(QMouseEvent* e) override;
void mouseReleaseEvent(QMouseEvent* e) override;
void mouseMoveEvent(QMouseEvent* e) override;
void wheelEvent(QWheelEvent* e) override;
void DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, bool dashed = false, GLfloat pointSize = 1.0f);
void DrawPointOrLine(const QVector4D& col, const std::vector<float>& vertices, int drawType, bool dashed = false, GLfloat pointSize = 1.0f);
private:
void SetDimensions(int w, int h);
bool Allocate(bool force = false);
bool Deallocate();
void SetViewport();
void DrawUnitSquare();
void DrawAffineHelper(int index, float circleWidth, float lineWidth, bool selected, bool hovered, bool pre, bool final, bool background);
GLEmberControllerBase* GLController();
bool m_Init = false;
bool m_Drawing = false;
GLint m_MaxTexSize = 16384;
GLint m_TexWidth = 0;
GLint m_TexHeight = 0;
GLint m_ViewWidth = 0;
GLint m_ViewHeight = 0;
GLuint m_OutputTexID = 0;
#ifdef USE_GLSL
GLuint m_PosAttr;
GLuint m_ColAttr;
GLuint m_PointSizeUniform;
GLuint m_MatrixUniform;
GLuint m_TexturePosAttr;
GLuint m_TextureUniform;
GLuint m_TextureMatrixUniform;
glm::ivec4 m_Viewport;
QMatrix4x4 m_ProjMatrix;
QMatrix4x4 m_ModelViewMatrix;
QMatrix4x4 m_ModelViewProjectionMatrix;
QMatrix4x4 m_TextureProjMatrix;
vector<float> m_Verts;
std::array<GLfloat, 10> m_TexVerts = std::array<GLfloat, 10>
{
0, 0,
0, 1,
1, 1,
1, 0,
0, 0
};
QOpenGLShaderProgram* m_Program = nullptr;
QOpenGLShaderProgram* m_QuadProgram = nullptr;
#endif
Fractorium* m_Fractorium = nullptr;
};

View File

@ -1,204 +1,207 @@
#include "FractoriumPch.h"
#include "LibraryTreeWidget.h"
#include "Fractorium.h"
/// <summary>
/// Set a pointer to the main window.
/// </summary>
/// <param name="f">Pointer to the main Fractorium object</param>
void LibraryTreeWidget::SetMainWindow(Fractorium* f)
{
m_Fractorium = f;
}
/// <summary>
/// Process the drop event to allow for moving items around inside of the tree.
/// </summary>
/// <param name="de">Pointer to the QDropEvent object</param>
void LibraryTreeWidget::dropEvent(QDropEvent* de)
{
const auto droppedIndex = indexAt(de->pos());
const auto items = selectionModel()->selectedRows();
if (!droppedIndex.isValid())//Don't process drop because it's outside of the droppable area.
{
de->ignore();
return;
}
else if (!items.empty())//Actually do the drop and move the item to a new location.
{
// get the list of the items that are about to be dragged
int i, row = droppedIndex.row();
DropIndicatorPosition dp = dropIndicatorPosition();
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dp == QAbstractItemView::BelowItem)
row++;
const auto itemat = this->itemFromIndex(droppedIndex);
QTreeWidget::dropEvent(de);//This internally changes the order of the items.
//Qt has a long standing major bug that rearranges the order of disjoint selections when
//The drop location is in between the disjoint regions.
//This is an attempt to correct for that bug by removing the dropped items, then re-inserting them
//in the order they were selected.
//This bug remains present as of Qt 5.8: https://bugreports.qt.io/browse/QTBUG-45320
if (const auto top = topLevelItem(0))
{
if (itemat)
{
const auto offsetitem = this->indexFromItem(itemat);
if (dp == QAbstractItemView::BelowItem)
{
const auto itemrow = offsetitem.row() + 1;
for (i = 0; i < dragItems.size(); i++)
{
if (itemrow < top->childCount())
top->takeChild(itemrow);
}
for (i = 0; i < dragItems.size(); i++)
{
const auto offset = i + itemrow;
if (offset <= top->childCount())
top->insertChild(offset, dragItems[i]);
}
}
else
{
const auto itemrow = offsetitem.row();//Will be at least 1 if dropped above it.
auto offset = itemrow;
for (i = 0; i < dragItems.size() && offset > 0; i++)
{
offset--;
if (offset < top->childCount())
top->takeChild(offset);
}
for (i = 0; i < dragItems.size(); i++)
{
if (offset <= top->childCount())
top->insertChild(offset, dragItems[i]);
offset++;
}
}
}
}
m_Fractorium->m_Controller->MoveLibraryItems(items, row);
}
}
/// <summary>
/// Set a pointer to the main window.
/// </summary>
/// <param name="f">Pointer to the main Fractorium object</param>
void InfoTreeWidget::SetMainWindow(Fractorium* f)
{
m_Fractorium = f;
}
/// <summary>
/// Called on each mouse movement while dragging, validate whether the area
/// being dragged over can be dropped on.
/// Can only drop on like (pre/reg/post) variation section of the same xform
/// of the variation being dragged.
/// </summary>
/// <param name="dme">Pointer to the drag move event</param>
void InfoTreeWidget::dragMoveEvent(QDragMoveEvent* dme)
{
QModelIndex index = indexAt(dme->pos());
if (!index.isValid())//Don't process drop because it's outside of the droppable area.
{
dme->ignore();
return;
}
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dragItems.size())
{
auto drag0 = dragItems[0];
if (auto itemat = itemFromIndex(index))
{
auto dragpre = drag0->text(0).startsWith("pre_", Qt::CaseInsensitive);
auto droppre = itemat->text(0).startsWith("pre_", Qt::CaseInsensitive);
auto dragpost = drag0->text(0).startsWith("post_", Qt::CaseInsensitive);
auto droppost = itemat->text(0).startsWith("post_", Qt::CaseInsensitive);
if (auto par = itemat->parent())
{
if (drag0->parent() == par &&
(par->text(0).startsWith("xform ", Qt::CaseInsensitive) ||
par->text(0).startsWith("final", Qt::CaseInsensitive)))
{
if (auto vitemat = dynamic_cast<VariationTreeWidgetItem*>(itemat))
{
bool dopre = dragpre && droppre;
bool dopost = dragpost && droppost;
bool doreg = !dragpre && !droppre && !dragpost && !droppost;
if (dopre || doreg || dopost)
{
QTreeWidget::dragMoveEvent(dme);
return;
}
}
}
}
}
}
dme->ignore();
}
/// <summary>
/// Process the drop event to allow for moving items around inside of the tree.
/// This will only allow variations to be moved around within the variation section of the tree
/// and nowhere else.
/// </summary>
/// <param name="de">Pointer to the QDropEvent object</param>
void InfoTreeWidget::dropEvent(QDropEvent* de)
{
QModelIndex droppedIndex = indexAt(de->pos());
auto items = selectionModel()->selectedRows();
if (!droppedIndex.isValid())//Don't process drop because it's outside of the droppable area.
{
de->ignore();
return;
}
else if (!items.empty())//Actually do the drop and move the item to a new location.
{
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dragItems.size())
{
auto drag0 = dragItems[0];
auto itemat = itemFromIndex(droppedIndex);
if (auto par = itemat->parent())
{
if (auto vdropitem = dynamic_cast<VariationTreeWidgetItem*>(itemat))
{
if (auto vdragitem = dynamic_cast<VariationTreeWidgetItem*>(drag0))
{
QTreeWidget::dropEvent(de);//This internally changes the order of the items.
m_Fractorium->ReorderVariations(par);
return;
}
}
}
}
de->ignore();
}
}
#include "FractoriumPch.h"
#include "LibraryTreeWidget.h"
#include "Fractorium.h"
/// <summary>
/// Set a pointer to the main window.
/// </summary>
/// <param name="f">Pointer to the main Fractorium object</param>
void LibraryTreeWidget::SetMainWindow(Fractorium* f)
{
m_Fractorium = f;
}
/// <summary>
/// Process the drop event to allow for moving items around inside of the tree.
/// </summary>
/// <param name="de">Pointer to the QDropEvent object</param>
void LibraryTreeWidget::dropEvent(QDropEvent* de)
{
const auto droppedIndex = indexAt(de->position().toPoint());
const auto items = selectionModel()->selectedRows();
if (!droppedIndex.isValid())//Don't process drop because it's outside of the droppable area.
{
de->ignore();
return;
}
else if (!items.empty())//Actually do the drop and move the item to a new location.
{
// get the list of the items that are about to be dragged
int i, row = droppedIndex.row();
DropIndicatorPosition dp = dropIndicatorPosition();
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dp == QAbstractItemView::BelowItem)
row++;
const auto itemat = this->itemFromIndex(droppedIndex);
QTreeWidget::dropEvent(de);//This internally changes the order of the items.
//Qt has a long standing major bug that rearranges the order of disjoint selections when
//The drop location is in between the disjoint regions.
//This is an attempt to correct for that bug by removing the dropped items, then re-inserting them
//in the order they were selected.
//This bug remains present as of Qt 5.8: https://bugreports.qt.io/browse/QTBUG-45320
if (const auto top = topLevelItem(0))
{
if (itemat)
{
const auto offsetitem = this->indexFromItem(itemat);
if (dp == QAbstractItemView::BelowItem)
{
const auto itemrow = offsetitem.row() + 1;
for (i = 0; i < dragItems.size(); i++)
{
if (itemrow < top->childCount())
top->takeChild(itemrow);
}
for (i = 0; i < dragItems.size(); i++)
{
const auto offset = i + itemrow;
if (offset <= top->childCount())
top->insertChild(offset, dragItems[i]);
}
}
else
{
const auto itemrow = offsetitem.row();//Will be at least 1 if dropped above it.
auto offset = itemrow;
for (i = 0; i < dragItems.size() && offset > 0; i++)
{
offset--;
if (offset < top->childCount())
top->takeChild(offset);
}
for (i = 0; i < dragItems.size(); i++)
{
if (offset <= top->childCount())
top->insertChild(offset, dragItems[i]);
offset++;
}
}
}
}
m_Fractorium->m_Controller->MoveLibraryItems(items, row);
}
}
/// <summary>
/// Set a pointer to the main window.
/// </summary>
/// <param name="f">Pointer to the main Fractorium object</param>
void InfoTreeWidget::SetMainWindow(Fractorium* f)
{
m_Fractorium = f;
}
/// <summary>
/// Called on each mouse movement while dragging, validate whether the area
/// being dragged over can be dropped on.
/// Can only drop on like (pre/reg/post) variation section of the same xform
/// of the variation being dragged.
/// </summary>
/// <param name="dme">Pointer to the drag move event</param>
void InfoTreeWidget::dragMoveEvent(QDragMoveEvent* dme)
{
QModelIndex const index = indexAt(dme->position().toPoint());
if (!index.isValid())//Don't process drop because it's outside of the droppable area.
{
dme->ignore();
return;
}
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dragItems.size())
{
if (auto drag0 = dragItems[0])
{
if (auto itemat = itemFromIndex(index))
{
const auto dragpre = drag0->text(0).startsWith("pre_", Qt::CaseInsensitive);
const auto droppre = itemat->text(0).startsWith("pre_", Qt::CaseInsensitive);
const auto dragpost = drag0->text(0).startsWith("post_", Qt::CaseInsensitive);
const auto droppost = itemat->text(0).startsWith("post_", Qt::CaseInsensitive);
if (const auto par = itemat->parent())
{
if (drag0->parent() == par &&
(par->text(0).startsWith("xform ", Qt::CaseInsensitive) ||
par->text(0).startsWith("final", Qt::CaseInsensitive)))
{
if (auto vitemat = dynamic_cast<const VariationTreeWidgetItem*>(itemat))
{
bool const dopre = dragpre && droppre;
bool const dopost = dragpost && droppost;
bool const doreg = !dragpre && !droppre && !dragpost && !droppost;
if (dopre || doreg || dopost)
{
QTreeWidget::dragMoveEvent(dme);
return;
}
}
}
}
}
}
}
dme->ignore();
}
/// <summary>
/// Process the drop event to allow for moving items around inside of the tree.
/// This will only allow variations to be moved around within the variation section of the tree
/// and nowhere else.
/// </summary>
/// <param name="de">Pointer to the QDropEvent object</param>
void InfoTreeWidget::dropEvent(QDropEvent* de)
{
QModelIndex const droppedIndex = indexAt(de->position().toPoint());
auto items = selectionModel()->selectedRows();
if (!droppedIndex.isValid())//Don't process drop because it's outside of the droppable area.
{
de->ignore();
return;
}
else if (!items.empty())//Actually do the drop and move the item to a new location.
{
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dragItems.size())
{
auto drag0 = dragItems[0];
if (auto itemat = itemFromIndex(droppedIndex))
{
if (auto par = itemat->parent())
{
if (auto vdropitem = dynamic_cast<const VariationTreeWidgetItem*>(itemat))
{
if (auto vdragitem = dynamic_cast<const VariationTreeWidgetItem*>(drag0))
{
QTreeWidget::dropEvent(de);//This internally changes the order of the items.
m_Fractorium->ReorderVariations(par);
return;
}
}
}
}
}
de->ignore();
}
}

View File

@ -1,54 +1,54 @@
#pragma once
#include "FractoriumPch.h"
class Fractorium;
/// <summary>
/// A thin derivation of QTreeWidget which allows for processing the drop event.
/// </summary>
class LibraryTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes p to the parent.
/// </summary>
/// <param name="p">The parent widget</param>
explicit LibraryTreeWidget(QWidget* p = nullptr)
: QTreeWidget(p)
{
}
void SetMainWindow(Fractorium* f);
protected:
virtual void dropEvent(QDropEvent* de) override;
Fractorium* m_Fractorium = nullptr;
};
class InfoTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes p to the parent.
/// </summary>
/// <param name="p">The parent widget</param>
explicit InfoTreeWidget(QWidget* p = nullptr)
: QTreeWidget(p)
{
}
void SetMainWindow(Fractorium* f);
const QString& LastNonVarField() const { return m_LastNonVarField; }
protected:
virtual void dropEvent(QDropEvent* de) override;
virtual void dragMoveEvent(QDragMoveEvent* dme) override;
Fractorium* m_Fractorium = nullptr;
QString m_LastNonVarField = "Direct color";//It is critical to update this if any more fields are ever added before the variations start.
};
#pragma once
#include "FractoriumPch.h"
class Fractorium;
/// <summary>
/// A thin derivation of QTreeWidget which allows for processing the drop event.
/// </summary>
class LibraryTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes p to the parent.
/// </summary>
/// <param name="p">The parent widget</param>
explicit LibraryTreeWidget(QWidget* p = nullptr)
: QTreeWidget(p)
{
}
void SetMainWindow(Fractorium* f);
protected:
virtual void dropEvent(QDropEvent* de) override;
Fractorium* m_Fractorium = nullptr;
};
class InfoTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes p to the parent.
/// </summary>
/// <param name="p">The parent widget</param>
explicit InfoTreeWidget(QWidget* p = nullptr)
: QTreeWidget(p)
{
}
void SetMainWindow(Fractorium* f);
const QString& LastNonVarField() const { return m_LastNonVarField; }
protected:
virtual void dropEvent(QDropEvent* de) override;
virtual void dragMoveEvent(QDragMoveEvent* dme) override;
Fractorium* m_Fractorium = nullptr;
QString m_LastNonVarField = "Direct color";//It is critical to update this if any more fields are ever added before the variations start.
};

View File

@ -1,79 +1,78 @@
#include "FractoriumPch.h"
#include "Fractorium.h"
#include <QtWidgets/QApplication>
#include <xmmintrin.h>
#include <immintrin.h>
#include <pmmintrin.h>
#ifdef __APPLE__
/// <summary>
/// Export default user data to ./config/fractorium
/// </summary>
void ExportUserData()
{
auto exec = new QProcess();
exec->setWorkingDirectory(QCoreApplication::applicationDirPath());
exec->start("/bin/sh", QStringList() << "fractorium-sh");
}
#endif
/// <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[])
{
int rv = -1;
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
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
#ifdef ISAAC_FLAM3_DEBUG
QMessageBox::critical(QApplication::desktop(), "Error", "Fractorium cannot be run in test mode, undefine ISAAC_FLAM3_DEBUG first.");
return 1;
#endif
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
auto vf = VarFuncs<float>::Instance();//Create instances that will stay alive until the program exits.
auto vlf = VariationList<float>::Instance();
auto palf = PaletteList<float>::Instance();
auto settings = FractoriumSettings::Instance();
#ifdef DO_DOUBLE
auto vd = VarFuncs<float>::Instance();
auto vld = VariationList<double>::Instance();//No further creations should occur after this.
#endif
try
{
//Required for large allocs, else GPU memory usage will be severely limited to small sizes.
//This must be done in the application and not in the EmberCL DLL.
#ifdef _WIN32
_putenv_s("GPU_MAX_ALLOC_PERCENT", "100");
#else
putenv(const_cast<char*>("GPU_MAX_ALLOC_PERCENT=100"));
#endif
Fractorium w;
w.show();
#ifdef __APPLE__
// exporting user data
ExportUserData();
#endif
a.installEventFilter(&w);
rv = a.exec();
}
catch (const std::exception& e)
{
QMessageBox::critical(nullptr, "Fatal Error", QString::fromStdString(e.what()));
}
catch (const char* e)
{
QMessageBox::critical(nullptr, "Fatal Error", e);
}
return rv;
}
#include "FractoriumPch.h"
#include "Fractorium.h"
#include <QtWidgets/QApplication>
#include <xmmintrin.h>
#include <immintrin.h>
#include <pmmintrin.h>
#ifdef __APPLE__
/// <summary>
/// Export default user data to ./config/fractorium
/// </summary>
void ExportUserData()
{
auto exec = new QProcess();
exec->setWorkingDirectory(QCoreApplication::applicationDirPath());
exec->start("/bin/sh", QStringList() << "fractorium-sh");
}
#endif
/// <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[])
{
int rv = -1;
QApplication a(argc, argv);
#ifdef TEST_CL
QMessageBox::critical(QApplication::desktop(), "Error", "Fractorium cannot be run in test mode, undefine TEST_CL first.");
return 1;
#endif
#ifdef ISAAC_FLAM3_DEBUG
QMessageBox::critical(QApplication::desktop(), "Error", "Fractorium cannot be run in test mode, undefine ISAAC_FLAM3_DEBUG first.");
return 1;
#endif
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
auto vf = VarFuncs<float>::Instance();//Create instances that will stay alive until the program exits.
auto vlf = VariationList<float>::Instance();
auto palf = PaletteList<float>::Instance();
auto settings = FractoriumSettings::Instance();
#ifdef DO_DOUBLE
auto vd = VarFuncs<float>::Instance();
auto vld = VariationList<double>::Instance();//No further creations should occur after this.
#endif
try
{
//Required for large allocs, else GPU memory usage will be severely limited to small sizes.
//This must be done in the application and not in the EmberCL DLL.
#ifdef _WIN32
_putenv_s("GPU_MAX_ALLOC_PERCENT", "100");
#else
putenv(const_cast<char*>("GPU_MAX_ALLOC_PERCENT=100"));
#endif
Fractorium w;
w.show();
#ifdef __APPLE__
// exporting user data
ExportUserData();
#endif
a.installEventFilter(&w);
rv = a.exec();
}
catch (const std::exception& e)
{
QMessageBox::critical(nullptr, "Fatal Error", QString::fromStdString(e.what()));
}
catch (const char* e)
{
QMessageBox::critical(nullptr, "Fatal Error", e);
}
return rv;
}

View File

@ -1,269 +1,269 @@
#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="p">The parent widget. Default: nullptr.</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumOptionsDialog::FractoriumOptionsDialog(QWidget* p, Qt::WindowFlags f)
: QDialog(p, f)
{
int row = 0;
const auto spinHeight = 20;
ui.setupUi(this);
m_Settings = FractoriumSettings::DefInstance();
m_Info = OpenCLInfo::Instance();
auto table = ui.OptionsXmlSavingTable;
ui.ThreadCountSpin->setRange(1, Timing::ProcessorCount());
connect(ui.OpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.DeviceTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnDeviceTableCellChanged(int, int)), Qt::QueuedConnection);
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);
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
ui.OptionsIdentityTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
table = ui.DeviceTable;
table->clearContents();
table->setRowCount(0);
if (m_Info->Ok() && !m_Info->Devices().empty())
{
SetupDeviceTable(table, m_Settings->Devices());
for (auto i = 0; i < table->rowCount(); i++)
if (auto radio = qobject_cast<QRadioButton*>(table->cellWidget(i, 1)))
connect(radio, SIGNAL(toggled(bool)), this, SLOT(OnDeviceTableRadioToggled(bool)), Qt::QueuedConnection);
}
else
{
ui.DeviceTable->setEnabled(false);
ui.OpenCLCheckBox->setChecked(false);
ui.OpenCLCheckBox->setEnabled(false);
ui.SharedTextureCheckBox->setChecked(false);
ui.SharedTextureCheckBox->setEnabled(false);
ui.OpenCLSubBatchPctSpin->setEnabled(false);
ui.OpenCLSubBatchSpin->setEnabled(false);
ui.OpenCLQualitySpin->setEnabled(false);
ui.OpenCLFilteringDERadioButton->setEnabled(false);
ui.OpenCLFilteringLogRadioButton->setEnabled(false);
ui.InteraciveGpuFilteringGroupBox->setEnabled(false);
}
DataToGui();
OnOpenCLCheckBoxStateChanged(ui.OpenCLCheckBox->isChecked());
}
/// <summary>
/// GUI settings wrapper functions, getters only.
/// </summary>
bool FractoriumOptionsDialog::EarlyClip() { return ui.EarlyClipCheckBox->isChecked(); }
bool FractoriumOptionsDialog::YAxisUp() { return ui.YAxisUpCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Transparency() { return ui.TransparencyCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ContinuousUpdate() { return ui.ContinuousUpdateCheckBox->isChecked(); }
bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked(); }
bool FractoriumOptionsDialog::SharedTexture() { return ui.SharedTextureCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Png16Bit() { return ui.Png16BitCheckBox->isChecked(); }
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
bool FractoriumOptionsDialog::LoadLast() { return ui.LoadLastOnStartCheckBox->isChecked(); }
bool FractoriumOptionsDialog::RotateAndScale() { return ui.RotateAndScaleCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Flam3Compat() { return ui.Flam3CompatCheckBox->isChecked(); }
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); }
uint FractoriumOptionsDialog::CpuQuality() { return ui.CpuQualitySpin->value(); }
uint FractoriumOptionsDialog::OpenClQuality() { return ui.OpenCLQualitySpin->value(); }
/// <summary>
/// The check state of one of the OpenCL devices was changed.
/// This does a special check to always ensure at least one device,
/// as well as one primary is checked.
/// </summary>
/// <param name="row">The row of the cell</param>
/// <param name="col">The column of the cell</param>
void FractoriumOptionsDialog::OnDeviceTableCellChanged(int row, int col)
{
if (auto item = ui.DeviceTable->item(row, col))
HandleDeviceTableCheckChanged(ui.DeviceTable, row, col);
}
/// <summary>
/// The primary device radio button selection was changed.
/// If the device was specified as primary, but was not selected
/// for inclusion, it will automatically be selected for inclusion.
/// </summary>
/// <param name="checked">The state of the radio button</param>
void FractoriumOptionsDialog::OnDeviceTableRadioToggled(bool checked)
{
int row;
const auto s = sender();
const auto table = ui.DeviceTable;
QRadioButton* radio = nullptr;
if (s)
{
for (row = 0; row < table->rowCount(); row++)
if (radio = qobject_cast<QRadioButton*>(table->cellWidget(row, 1)))
if (s == radio)
{
HandleDeviceTableCheckChanged(ui.DeviceTable, row, 1);
break;
}
}
}
/// <summary>
/// Disable or enable the CPU and 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)
{
const auto checked = state == Qt::Checked;
ui.DeviceTable->setEnabled(checked);
ui.ThreadCountSpin->setEnabled(!checked);
ui.CpuSubBatchSpin->setEnabled(!checked);
ui.SharedTextureCheckBox->setEnabled(checked);
ui.OpenCLSubBatchPctSpin->setEnabled(checked);
ui.OpenCLSubBatchSpin->setEnabled(checked);
ui.OpenCLQualitySpin->setEnabled(checked);
ui.CpuQualitySpin->setEnabled(!checked);
ui.CpuFilteringDERadioButton->setEnabled(!checked);
ui.CpuFilteringLogRadioButton->setEnabled(!checked);
ui.OpenCLFilteringDERadioButton->setEnabled(checked);
ui.OpenCLFilteringLogRadioButton->setEnabled(checked);
ui.InteraciveCpuFilteringGroupBox->setEnabled(!checked);
ui.InteraciveGpuFilteringGroupBox->setEnabled(checked);
}
/// <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()
{
GuiToData();
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()
{
DataToGui();
QDialog::reject();
}
/// <summary>
/// Copy the state of the map to the checkboxes and show the dialog.
/// </summary>
/// <param name="e">Event, passed to base.</param>
void FractoriumOptionsDialog::showEvent(QShowEvent* e)
{
DataToGui();
QDialog::showEvent(e);
}
/// <summary>
/// Copy the state of the Gui to the settings object.
/// </summary>
void FractoriumOptionsDialog::GuiToData()
{
//Interactive rendering.
m_Settings->EarlyClip(EarlyClip());
m_Settings->YAxisUp(YAxisUp());
m_Settings->Transparency(Transparency());
m_Settings->ContinuousUpdate(ContinuousUpdate());
m_Settings->OpenCL(OpenCL());
m_Settings->SharedTexture(SharedTexture());
m_Settings->Double(Double());
m_Settings->ShowAllXforms(ShowAllXforms());
m_Settings->ToggleType(ToggleType());
m_Settings->Png16Bit(Png16Bit());
m_Settings->ThreadCount(ThreadCount());
m_Settings->RandomCount(RandomCount());
m_Settings->LoadLast(LoadLast());
m_Settings->RotateAndScale(RotateAndScale());
m_Settings->Flam3Compat(Flam3Compat());
m_Settings->CpuQuality(CpuQuality());
m_Settings->OpenClQuality(OpenClQuality());
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
m_Settings->OpenCLSubBatchPct(ui.OpenCLSubBatchPctSpin->value());
m_Settings->OpenCLSubBatch(ui.OpenCLSubBatchSpin->value());
m_Settings->CpuDEFilter(ui.CpuFilteringDERadioButton->isChecked());
m_Settings->OpenCLDEFilter(ui.OpenCLFilteringDERadioButton->isChecked());
m_Settings->Devices(DeviceTableToSettings(ui.DeviceTable));
//Xml saving.
m_Settings->XmlTemporalSamples(m_XmlTemporalSamplesSpin->value());
m_Settings->XmlQuality(m_XmlQualitySpin->value());
m_Settings->XmlSupersample(m_XmlSupersampleSpin->value());
m_Settings->SaveAutoUnique(AutoUnique());
//Identity.
m_Settings->Id(m_IdEdit->text());
m_Settings->Url(m_UrlEdit->text());
m_Settings->Nick(m_NickEdit->text());
}
/// <summary>
/// Copy the state of the settings object to the Gui.
/// </summary>
void FractoriumOptionsDialog::DataToGui()
{
//Interactive rendering.
const auto devices = m_Settings->Devices();
ui.EarlyClipCheckBox->setChecked(m_Settings->EarlyClip());
ui.YAxisUpCheckBox->setChecked(m_Settings->YAxisUp());
ui.TransparencyCheckBox->setChecked(m_Settings->Transparency());
ui.ContinuousUpdateCheckBox->setChecked(m_Settings->ContinuousUpdate());
ui.OpenCLCheckBox->setChecked(m_Settings->OpenCL());
ui.SharedTextureCheckBox->setChecked(m_Settings->SharedTexture());
ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double());
ui.ShowAllXformsCheckBox->setChecked(m_Settings->ShowAllXforms());
ui.ToggleTypeCheckBox->setChecked(m_Settings->ToggleType());
ui.Png16BitCheckBox->setChecked(m_Settings->Png16Bit());
ui.ThreadCountSpin->setValue(m_Settings->ThreadCount());
ui.RandomCountSpin->setValue(m_Settings->RandomCount());
ui.LoadLastOnStartCheckBox->setChecked(m_Settings->LoadLast());
ui.RotateAndScaleCheckBox->setChecked(m_Settings->RotateAndScale());
ui.Flam3CompatCheckBox->setChecked(m_Settings->Flam3Compat());
ui.CpuQualitySpin->setValue(m_Settings->CpuQuality());
ui.OpenCLQualitySpin->setValue(m_Settings->OpenClQuality());
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
ui.OpenCLSubBatchPctSpin->setValue(m_Settings->OpenCLSubBatchPct());
ui.OpenCLSubBatchSpin->setValue(m_Settings->OpenCLSubBatch());
SettingsToDeviceTable(ui.DeviceTable, devices);
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);
//Xml saving.
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
m_XmlQualitySpin->setValue(m_Settings->XmlQuality());
m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample());
ui.AutoUniqueCheckBox->setChecked(m_Settings->SaveAutoUnique());
//Identity.
m_IdEdit->setText(m_Settings->Id());
m_UrlEdit->setText(m_Settings->Url());
m_NickEdit->setText(m_Settings->Nick());
}
#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="p">The parent widget. Default: nullptr.</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumOptionsDialog::FractoriumOptionsDialog(QWidget* p, Qt::WindowFlags f)
: QDialog(p, f)
{
int row = 0;
const auto spinHeight = 20;
ui.setupUi(this);
m_Settings = FractoriumSettings::DefInstance();
m_Info = OpenCLInfo::Instance();
auto table = ui.OptionsXmlSavingTable;
ui.ThreadCountSpin->setRange(1, Timing::ProcessorCount());
connect(ui.OpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.DeviceTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnDeviceTableCellChanged(int, int)), Qt::QueuedConnection);
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);
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
ui.OptionsIdentityTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
table = ui.DeviceTable;
table->clearContents();
table->setRowCount(0);
if (m_Info->Ok() && !m_Info->Devices().empty())
{
SetupDeviceTable(table, m_Settings->Devices());
for (auto i = 0; i < table->rowCount(); i++)
if (auto radio = qobject_cast<QRadioButton*>(table->cellWidget(i, 1)))
connect(radio, SIGNAL(toggled(bool)), this, SLOT(OnDeviceTableRadioToggled(bool)), Qt::QueuedConnection);
}
else
{
ui.DeviceTable->setEnabled(false);
ui.OpenCLCheckBox->setChecked(false);
ui.OpenCLCheckBox->setEnabled(false);
ui.SharedTextureCheckBox->setChecked(false);
ui.SharedTextureCheckBox->setEnabled(false);
ui.OpenCLSubBatchPctSpin->setEnabled(false);
ui.OpenCLSubBatchSpin->setEnabled(false);
ui.OpenCLQualitySpin->setEnabled(false);
ui.OpenCLFilteringDERadioButton->setEnabled(false);
ui.OpenCLFilteringLogRadioButton->setEnabled(false);
ui.InteraciveGpuFilteringGroupBox->setEnabled(false);
}
DataToGui();
OnOpenCLCheckBoxStateChanged(ui.OpenCLCheckBox->isChecked());
}
/// <summary>
/// GUI settings wrapper functions, getters only.
/// </summary>
bool FractoriumOptionsDialog::EarlyClip() { return ui.EarlyClipCheckBox->isChecked(); }
bool FractoriumOptionsDialog::YAxisUp() { return ui.YAxisUpCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Transparency() { return ui.TransparencyCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ContinuousUpdate() { return ui.ContinuousUpdateCheckBox->isChecked(); }
bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked(); }
bool FractoriumOptionsDialog::SharedTexture() { return ui.SharedTextureCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Png16Bit() { return ui.Png16BitCheckBox->isChecked(); }
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
bool FractoriumOptionsDialog::LoadLast() { return ui.LoadLastOnStartCheckBox->isChecked(); }
bool FractoriumOptionsDialog::RotateAndScale() { return ui.RotateAndScaleCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Flam3Compat() { return ui.Flam3CompatCheckBox->isChecked(); }
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); }
uint FractoriumOptionsDialog::CpuQuality() { return ui.CpuQualitySpin->value(); }
uint FractoriumOptionsDialog::OpenClQuality() { return ui.OpenCLQualitySpin->value(); }
/// <summary>
/// The check state of one of the OpenCL devices was changed.
/// This does a special check to always ensure at least one device,
/// as well as one primary is checked.
/// </summary>
/// <param name="row">The row of the cell</param>
/// <param name="col">The column of the cell</param>
void FractoriumOptionsDialog::OnDeviceTableCellChanged(int row, int col)
{
if (auto item = ui.DeviceTable->item(row, col))
HandleDeviceTableCheckChanged(ui.DeviceTable, row, col);
}
/// <summary>
/// The primary device radio button selection was changed.
/// If the device was specified as primary, but was not selected
/// for inclusion, it will automatically be selected for inclusion.
/// </summary>
/// <param name="checked">The state of the radio button</param>
void FractoriumOptionsDialog::OnDeviceTableRadioToggled(bool checked)
{
int row;
const auto s = sender();
const auto table = ui.DeviceTable;
QRadioButton* radio = nullptr;
if (s)
{
for (row = 0; row < table->rowCount(); row++)
if (radio = qobject_cast<QRadioButton*>(table->cellWidget(row, 1)))
if (s == radio)
{
HandleDeviceTableCheckChanged(ui.DeviceTable, row, 1);
break;
}
}
}
/// <summary>
/// Disable or enable the CPU and 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)
{
const auto checked = state == Qt::Checked;
ui.DeviceTable->setEnabled(checked);
ui.ThreadCountSpin->setEnabled(!checked);
ui.CpuSubBatchSpin->setEnabled(!checked);
ui.SharedTextureCheckBox->setEnabled(checked);
ui.OpenCLSubBatchPctSpin->setEnabled(checked);
ui.OpenCLSubBatchSpin->setEnabled(checked);
ui.OpenCLQualitySpin->setEnabled(checked);
ui.CpuQualitySpin->setEnabled(!checked);
ui.CpuFilteringDERadioButton->setEnabled(!checked);
ui.CpuFilteringLogRadioButton->setEnabled(!checked);
ui.OpenCLFilteringDERadioButton->setEnabled(checked);
ui.OpenCLFilteringLogRadioButton->setEnabled(checked);
ui.InteraciveCpuFilteringGroupBox->setEnabled(!checked);
ui.InteraciveGpuFilteringGroupBox->setEnabled(checked);
}
/// <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()
{
GuiToData();
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()
{
DataToGui();
QDialog::reject();
}
/// <summary>
/// Copy the state of the map to the checkboxes and show the dialog.
/// </summary>
/// <param name="e">Event, passed to base.</param>
void FractoriumOptionsDialog::showEvent(QShowEvent* e)
{
DataToGui();
QDialog::showEvent(e);
}
/// <summary>
/// Copy the state of the Gui to the settings object.
/// </summary>
void FractoriumOptionsDialog::GuiToData()
{
//Interactive rendering.
m_Settings->EarlyClip(EarlyClip());
m_Settings->YAxisUp(YAxisUp());
m_Settings->Transparency(Transparency());
m_Settings->ContinuousUpdate(ContinuousUpdate());
m_Settings->OpenCL(OpenCL());
m_Settings->SharedTexture(SharedTexture());
m_Settings->Double(Double());
m_Settings->ShowAllXforms(ShowAllXforms());
m_Settings->ToggleType(ToggleType());
m_Settings->Png16Bit(Png16Bit());
m_Settings->ThreadCount(ThreadCount());
m_Settings->RandomCount(RandomCount());
m_Settings->LoadLast(LoadLast());
m_Settings->RotateAndScale(RotateAndScale());
m_Settings->Flam3Compat(Flam3Compat());
m_Settings->CpuQuality(CpuQuality());
m_Settings->OpenClQuality(OpenClQuality());
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
m_Settings->OpenCLSubBatchPct(ui.OpenCLSubBatchPctSpin->value());
m_Settings->OpenCLSubBatch(ui.OpenCLSubBatchSpin->value());
m_Settings->CpuDEFilter(ui.CpuFilteringDERadioButton->isChecked());
m_Settings->OpenCLDEFilter(ui.OpenCLFilteringDERadioButton->isChecked());
m_Settings->Devices(DeviceTableToSettings(ui.DeviceTable));
//Xml saving.
m_Settings->XmlTemporalSamples(m_XmlTemporalSamplesSpin->value());
m_Settings->XmlQuality(m_XmlQualitySpin->value());
m_Settings->XmlSupersample(m_XmlSupersampleSpin->value());
m_Settings->SaveAutoUnique(AutoUnique());
//Identity.
m_Settings->Id(m_IdEdit->text());
m_Settings->Url(m_UrlEdit->text());
m_Settings->Nick(m_NickEdit->text());
}
/// <summary>
/// Copy the state of the settings object to the Gui.
/// </summary>
void FractoriumOptionsDialog::DataToGui()
{
//Interactive rendering.
const auto devices = m_Settings->Devices();
ui.EarlyClipCheckBox->setChecked(m_Settings->EarlyClip());
ui.YAxisUpCheckBox->setChecked(m_Settings->YAxisUp());
ui.TransparencyCheckBox->setChecked(m_Settings->Transparency());
ui.ContinuousUpdateCheckBox->setChecked(m_Settings->ContinuousUpdate());
ui.OpenCLCheckBox->setChecked(m_Settings->OpenCL());
ui.SharedTextureCheckBox->setChecked(m_Settings->SharedTexture());
ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double());
ui.ShowAllXformsCheckBox->setChecked(m_Settings->ShowAllXforms());
ui.ToggleTypeCheckBox->setChecked(m_Settings->ToggleType());
ui.Png16BitCheckBox->setChecked(m_Settings->Png16Bit());
ui.ThreadCountSpin->setValue(m_Settings->ThreadCount());
ui.RandomCountSpin->setValue(m_Settings->RandomCount());
ui.LoadLastOnStartCheckBox->setChecked(m_Settings->LoadLast());
ui.RotateAndScaleCheckBox->setChecked(m_Settings->RotateAndScale());
ui.Flam3CompatCheckBox->setChecked(m_Settings->Flam3Compat());
ui.CpuQualitySpin->setValue(m_Settings->CpuQuality());
ui.OpenCLQualitySpin->setValue(m_Settings->OpenClQuality());
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
ui.OpenCLSubBatchPctSpin->setValue(m_Settings->OpenCLSubBatchPct());
ui.OpenCLSubBatchSpin->setValue(m_Settings->OpenCLSubBatch());
SettingsToDeviceTable(ui.DeviceTable, devices);
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);
//Xml saving.
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
m_XmlQualitySpin->setValue(m_Settings->XmlQuality());
m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample());
ui.AutoUniqueCheckBox->setChecked(m_Settings->SaveAutoUnique());
//Identity.
m_IdEdit->setText(m_Settings->Id());
m_UrlEdit->setText(m_Settings->Url());
m_NickEdit->setText(m_Settings->Nick());
}

View File

@ -1,69 +1,69 @@
#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(QWidget* p = nullptr, Qt::WindowFlags f = 0);
bool EarlyClip();
bool YAxisUp();
bool Transparency();
bool ContinuousUpdate();
bool OpenCL();
bool SharedTexture();
bool Double();
bool ShowAllXforms();
bool ToggleType();
bool Png16Bit();
bool AutoUnique();
bool LoadLast();
bool RotateAndScale();
bool Flam3Compat();
uint ThreadCount();
uint RandomCount();
uint CpuQuality();
uint OpenClQuality();
void DataToGui();
void GuiToData();
public slots:
void OnOpenCLCheckBoxStateChanged(int state);
void OnDeviceTableCellChanged(int row, int col);
void OnDeviceTableRadioToggled(bool checked);
void accept() override;
void reject() override;
protected:
void showEvent(QShowEvent* e) override;
private:
Ui::OptionsDialog ui;
shared_ptr<OpenCLInfo> m_Info;
SpinBox* m_XmlTemporalSamplesSpin;
SpinBox* m_XmlQualitySpin;
SpinBox* m_XmlSupersampleSpin;
QLineEdit* m_IdEdit;
QLineEdit* m_UrlEdit;
QLineEdit* m_NickEdit;
shared_ptr<FractoriumSettings> m_Settings;
};
#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(QWidget* p = nullptr, Qt::WindowFlags f = Qt::WindowType::Widget);
bool EarlyClip();
bool YAxisUp();
bool Transparency();
bool ContinuousUpdate();
bool OpenCL();
bool SharedTexture();
bool Double();
bool ShowAllXforms();
bool ToggleType();
bool Png16Bit();
bool AutoUnique();
bool LoadLast();
bool RotateAndScale();
bool Flam3Compat();
uint ThreadCount();
uint RandomCount();
uint CpuQuality();
uint OpenClQuality();
void DataToGui();
void GuiToData();
public slots:
void OnOpenCLCheckBoxStateChanged(int state);
void OnDeviceTableCellChanged(int row, int col);
void OnDeviceTableRadioToggled(bool checked);
void accept() override;
void reject() override;
protected:
void showEvent(QShowEvent* e) override;
private:
Ui::OptionsDialog ui;
shared_ptr<OpenCLInfo> m_Info;
SpinBox* m_XmlTemporalSamplesSpin;
SpinBox* m_XmlQualitySpin;
SpinBox* m_XmlSupersampleSpin;
QLineEdit* m_IdEdit;
QLineEdit* m_UrlEdit;
QLineEdit* m_NickEdit;
shared_ptr<FractoriumSettings> m_Settings;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,391 +1,391 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PaletteEditor</class>
<widget class="QDialog" name="PaletteEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>859</width>
<height>540</height>
</rect>
</property>
<property name="windowTitle">
<string>Palette Editor</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QVBoxLayout" name="LeftSideVerticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QHBoxLayout" name="LeftTopHorizontalLayout">
<item>
<widget class="QPushButton" name="NewPaletteFileButton">
<property name="text">
<string>New Palette File...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="CopyPaletteFileButton">
<property name="text">
<string>Copy Palette File</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QComboBox" name="PaletteFilenameCombo">
<property name="maximumSize">
<size>
<width>365</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="PaletteListTable">
<property name="maximumSize">
<size>
<width>365</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<kerning>true</kerning>
</font>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="autoScroll">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AllEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>16</number>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>16</number>
</attribute>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Palette</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="LeftBottomHorizontalLayout">
<item>
<widget class="QPushButton" name="AppendPaletteButton">
<property name="text">
<string>Append Palette</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="OverwritePaletteButton">
<property name="text">
<string>Overwrite Palette</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="DeletePaletteButton">
<property name="text">
<string>Delete Palette</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="MainVerticalLayout">
<item>
<widget class="QGroupBox" name="ColorViewGroupBox">
<property name="title">
<string>Palette</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="ColorPickerGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="title">
<string>Color Picker</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="ControlsGroupBox">
<property name="title">
<string>Adjustment Controls</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,4">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QGridLayout" name="AdjustmentLeftGridLayout">
<item row="1" column="0">
<widget class="QCheckBox" name="AutoDistributeCheckBox">
<property name="text">
<string>Auto Distribute</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QSpinBox" name="ArrowsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::UpDownArrows</enum>
</property>
<property name="suffix">
<string/>
</property>
<property name="prefix">
<string>Arrows: </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>256</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="SyncCheckBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Sync</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="BlendCheckBox">
<property name="text">
<string>Blend</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="AdjustmentRightGridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="CreatePaletteFromImageButton">
<property name="text">
<string>Create From Image</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="ResetColorsButton">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="RandomColorsButton">
<property name="text">
<string>Random Colors</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="RemoveColorButton">
<property name="text">
<string>Remove Color</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="DistributeColorsButton">
<property name="text">
<string>Distribute Colors</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="AddColorButton">
<property name="text">
<string>Add Color</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="InvertColorsButton">
<property name="text">
<string>Invert Colors</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="CreatePaletteAgainFromImageButton">
<property name="text">
<string>Create Again From Image</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PaletteEditor</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PaletteEditor</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PaletteEditor</class>
<widget class="QDialog" name="PaletteEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>859</width>
<height>540</height>
</rect>
</property>
<property name="windowTitle">
<string>Palette Editor</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QVBoxLayout" name="LeftSideVerticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QHBoxLayout" name="LeftTopHorizontalLayout">
<item>
<widget class="QPushButton" name="NewPaletteFileButton">
<property name="text">
<string>New Palette File...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="CopyPaletteFileButton">
<property name="text">
<string>Copy Palette File</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QComboBox" name="PaletteFilenameCombo">
<property name="maximumSize">
<size>
<width>365</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="PaletteListTable">
<property name="maximumSize">
<size>
<width>365</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<kerning>true</kerning>
</font>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="autoScroll">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AllEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>16</number>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>16</number>
</attribute>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Palette</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="LeftBottomHorizontalLayout">
<item>
<widget class="QPushButton" name="AppendPaletteButton">
<property name="text">
<string>Append Palette</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="OverwritePaletteButton">
<property name="text">
<string>Overwrite Palette</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="DeletePaletteButton">
<property name="text">
<string>Delete Palette</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="MainVerticalLayout">
<item>
<widget class="QGroupBox" name="ColorViewGroupBox">
<property name="title">
<string>Palette</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="ColorPickerGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="title">
<string>Color Picker</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="ControlsGroupBox">
<property name="title">
<string>Adjustment Controls</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,4">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QGridLayout" name="AdjustmentLeftGridLayout">
<item row="1" column="0">
<widget class="QCheckBox" name="AutoDistributeCheckBox">
<property name="text">
<string>Auto Distribute</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QSpinBox" name="ArrowsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::UpDownArrows</enum>
</property>
<property name="suffix">
<string/>
</property>
<property name="prefix">
<string>Arrows: </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>256</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="SyncCheckBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Sync</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="BlendCheckBox">
<property name="text">
<string>Blend</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="AdjustmentRightGridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="CreatePaletteFromImageButton">
<property name="text">
<string>Create From Image</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="ResetColorsButton">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="RandomColorsButton">
<property name="text">
<string>Random Colors</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="RemoveColorButton">
<property name="text">
<string>Remove Color</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="DistributeColorsButton">
<property name="text">
<string>Distribute Colors</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="AddColorButton">
<property name="text">
<string>Add Color</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="InvertColorsButton">
<property name="text">
<string>Invert Colors</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="CreatePaletteAgainFromImageButton">
<property name="text">
<string>Create Again From Image</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PaletteEditor</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PaletteEditor</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1,79 +1,79 @@
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FractoriumPch.h"
#include "ColorPanel.h"
/// <summary>
/// Constructor which passes parent widget to the base and initializes the minimum size.
/// </summary>
/// <param name="p">The parent widget</param>
ColorPanel::ColorPanel(QWidget* p)
: QPushButton(p)
{
setMinimumSize(10, 10);
}
/// <summary>
/// Derived paint event which paints the entire button surface with m_Color.
/// </summary>
void ColorPanel::paintEvent(QPaintEvent*)
{
QPainter p(this);
p.setPen(m_Pen);
p.setBrush(QBrush(m_Color));
p.drawRect(QRect(2, 2, width() - 6, height() - 6));
}
/// <summary>
/// Set the pen object used to paint the button.
/// </summary>
/// <param name="pen">The pen object used to paint the button</param>
void ColorPanel::Pen(const QPen& pen)
{
m_Pen = pen;
update();
}
/// <summary>
/// Get the pen object used to paint the button.
/// </summary>
/// <returns>QPen</returns>
QPen ColorPanel::Pen() const
{
return m_Pen;
}
/// <summary>
/// Set the color used to paint the button.
/// </summary>
/// <param name="color">The color used to paint the button</param>
void ColorPanel::Color(const QColor& color)
{
m_Color = color;
update();
}
/// <summary>
/// Get the color used to paint the button.
/// </summary>
/// <returns>QColor</returns>
QColor ColorPanel::Color() const
{
return m_Color;
}
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FractoriumPch.h"
#include "ColorPanel.h"
/// <summary>
/// Constructor which passes parent widget to the base and initializes the minimum size.
/// </summary>
/// <param name="p">The parent widget</param>
ColorPanel::ColorPanel(QWidget* p)
: QPushButton(p)
{
setMinimumSize(10, 10);
}
/// <summary>
/// Derived paint event which paints the entire button surface with m_Color.
/// </summary>
void ColorPanel::paintEvent(QPaintEvent*)
{
QPainter p(this);
p.setPen(m_Pen);
p.setBrush(QBrush(m_Color));
p.drawRect(QRect(2, 2, width() - 6, height() - 6));
}
/// <summary>
/// Set the pen object used to paint the button.
/// </summary>
/// <param name="pen">The pen object used to paint the button</param>
void ColorPanel::Pen(const QPen& pen)
{
m_Pen = pen;
update();
}
/// <summary>
/// Get the pen object used to paint the button.
/// </summary>
/// <returns>QPen</returns>
QPen ColorPanel::Pen() const
{
return m_Pen;
}
/// <summary>
/// Set the color used to paint the button.
/// </summary>
/// <param name="color">The color used to paint the button</param>
void ColorPanel::Color(const QColor& color)
{
m_Color = color;
update();
}
/// <summary>
/// Get the color used to paint the button.
/// </summary>
/// <returns>QColor</returns>
QColor ColorPanel::Color() const
{
return m_Color;
}

View File

@ -1,47 +1,47 @@
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// A derivation of a QPushButton which makes a large, clickable panel
/// which custom paints the button based on a user specified color.
/// </summary>
class ColorPanel : public QPushButton
{
Q_OBJECT
public:
ColorPanel(QWidget* p = nullptr);
void Pen(const QPen& pen);
QPen Pen() const;
void Color(const QColor& color);
QColor Color() const;
protected:
void paintEvent(QPaintEvent* event) override;
private:
QPen m_Pen;
QColor m_Color;
};
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// A derivation of a QPushButton which makes a large, clickable panel
/// which custom paints the button based on a user specified color.
/// </summary>
class ColorPanel : public QPushButton
{
Q_OBJECT
public:
ColorPanel(QWidget* p = nullptr);
void Pen(const QPen& pen);
QPen Pen() const;
void Color(const QColor& color);
QColor Color() const;
protected:
void paintEvent(QPaintEvent* event) override;
private:
QPen m_Pen;
QColor m_Color;
};

View File

@ -1,97 +1,97 @@
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FractoriumPch.h"
#include "ColorPickerWidget.h"
/// <summary>
/// Constructor which passes parent widget to the base and initializes the member controls
/// on a grid layout.
/// </summary>
/// <param name="p">The parent widget</param>
ColorPickerWidget::ColorPickerWidget(QWidget* p)
: QWidget(p)
{
m_ColorTriangle = new ColorTriangle(this);
m_ColorPanel = new ColorPanel(this);
m_ColorDialog = new QColorDialog(this);
m_ColorPanel->Color(m_ColorTriangle->Color());
connect(m_ColorTriangle, SIGNAL(ColorChanged(const QColor&)), this, SLOT(OnTriangleColorChanged(const QColor&)));
connect(m_ColorPanel, SIGNAL(clicked()), this, SLOT(OnColorViewerClicked()));
auto layout = new QGridLayout(this);
layout->setMargin(4);
layout->addWidget(m_ColorTriangle, 0, 0, 3, 1);
layout->addWidget(m_ColorPanel, 0, 1, 3, 1);
setLayout(layout);
}
/// <summary>
/// Get the color used to paint the color panel.
/// </summary>
/// <returns>QColor</returns>
QColor ColorPickerWidget::Color() const
{
return m_ColorPanel->Color();
}
/// <summary>
/// Set the current color for the triangle.
/// </summary>
/// <param name="col">The parent widget</param>
void ColorPickerWidget::SetColorPanelColor(const QColor& col)
{
if (col.isValid())
m_ColorTriangle->Color(col);//Internally emits ColorChanged() which will call OnTriangleColorChanged(), which will call m_ColorPanel->Color().
}
/// <summary>
/// Overridden resize event to set the color panel height slightly
/// smaller than the container it's in.
/// </summary>
/// <param name="col">The resize event</param>
void ColorPickerWidget::resizeEvent(QResizeEvent* event)
{
m_ColorPanel->setMinimumHeight(event->size().height() - 22);
m_ColorPanel->setMaximumHeight(event->size().height() - 22);
}
/// <summary>
/// Slot called when the color panel is clicked, which will show the color
/// picker dialog.
/// </summary>
void ColorPickerWidget::OnColorViewerClicked()
{
m_ColorDialog->setCurrentColor(m_ColorPanel->Color());
const auto newColor = m_ColorDialog->getColor(m_ColorPanel->Color(), this);
SetColorPanelColor(newColor);
}
/// <summary>
/// Slot called when the color on the triangle changes for any reason,
/// either user initiated or programatically called.
/// </summary>
/// <param name="col">The new color on the triangle</param>
void ColorPickerWidget::OnTriangleColorChanged(const QColor& col)
{
if (col.isValid())
{
m_ColorPanel->Color(col);
emit ColorChanged(col);
}
}
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FractoriumPch.h"
#include "ColorPickerWidget.h"
/// <summary>
/// Constructor which passes parent widget to the base and initializes the member controls
/// on a grid layout.
/// </summary>
/// <param name="p">The parent widget</param>
ColorPickerWidget::ColorPickerWidget(QWidget* p)
: QWidget(p)
{
m_ColorTriangle = new ColorTriangle(this);
m_ColorPanel = new ColorPanel(this);
m_ColorDialog = new QColorDialog(this);
m_ColorPanel->Color(m_ColorTriangle->Color());
connect(m_ColorTriangle, SIGNAL(ColorChanged(const QColor&)), this, SLOT(OnTriangleColorChanged(const QColor&)));
connect(m_ColorPanel, SIGNAL(clicked()), this, SLOT(OnColorViewerClicked()));
auto layout = new QGridLayout(this);
layout->setContentsMargins(4, 4, 4, 4);
layout->addWidget(m_ColorTriangle, 0, 0, 3, 1);
layout->addWidget(m_ColorPanel, 0, 1, 3, 1);
setLayout(layout);
}
/// <summary>
/// Get the color used to paint the color panel.
/// </summary>
/// <returns>QColor</returns>
QColor ColorPickerWidget::Color() const
{
return m_ColorPanel->Color();
}
/// <summary>
/// Set the current color for the triangle.
/// </summary>
/// <param name="col">The parent widget</param>
void ColorPickerWidget::SetColorPanelColor(const QColor& col)
{
if (col.isValid())
m_ColorTriangle->Color(col);//Internally emits ColorChanged() which will call OnTriangleColorChanged(), which will call m_ColorPanel->Color().
}
/// <summary>
/// Overridden resize event to set the color panel height slightly
/// smaller than the container it's in.
/// </summary>
/// <param name="col">The resize event</param>
void ColorPickerWidget::resizeEvent(QResizeEvent* event)
{
m_ColorPanel->setMinimumHeight(event->size().height() - 22);
m_ColorPanel->setMaximumHeight(event->size().height() - 22);
}
/// <summary>
/// Slot called when the color panel is clicked, which will show the color
/// picker dialog.
/// </summary>
void ColorPickerWidget::OnColorViewerClicked()
{
m_ColorDialog->setCurrentColor(m_ColorPanel->Color());
const auto newColor = m_ColorDialog->getColor(m_ColorPanel->Color(), this);
SetColorPanelColor(newColor);
}
/// <summary>
/// Slot called when the color on the triangle changes for any reason,
/// either user initiated or programatically called.
/// </summary>
/// <param name="col">The new color on the triangle</param>
void ColorPickerWidget::OnTriangleColorChanged(const QColor& col)
{
if (col.isValid())
{
m_ColorPanel->Color(col);
emit ColorChanged(col);
}
}

View File

@ -1,54 +1,54 @@
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "FractoriumPch.h"
#include "ColorTriangle.h"
#include "ColorPanel.h"
/// <summary>
/// Aggregator class to package a color triangle, color panel, and color dialog
/// all together on a layout.
/// </summary>
class ColorPickerWidget : public QWidget
{
Q_OBJECT
public:
ColorPickerWidget(QWidget* p = nullptr);
QColor Color() const;
void SetColorPanelColor(const QColor& col);
Q_SIGNALS:
void ColorChanged(const QColor& col);
protected:
void resizeEvent(QResizeEvent* event) override;
private Q_SLOTS:
void OnColorViewerClicked();
void OnTriangleColorChanged(const QColor& col);
private:
ColorTriangle* m_ColorTriangle;
ColorPanel* m_ColorPanel;
QColorDialog* m_ColorDialog;
};
/*
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
This file is part of colorPickerWidget.
colorPickerWidget is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
colorPickerWidget is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "FractoriumPch.h"
#include "ColorTriangle.h"
#include "ColorPanel.h"
/// <summary>
/// Aggregator class to package a color triangle, color panel, and color dialog
/// all together on a layout.
/// </summary>
class ColorPickerWidget : public QWidget
{
Q_OBJECT
public:
ColorPickerWidget(QWidget* p = nullptr);
QColor Color() const;
void SetColorPanelColor(const QColor& col);
Q_SIGNALS:
void ColorChanged(const QColor& col);
protected:
void resizeEvent(QResizeEvent* event) override;
private Q_SLOTS:
void OnColorViewerClicked();
void OnTriangleColorChanged(const QColor& col);
private:
ColorTriangle* m_ColorTriangle;
ColorPanel* m_ColorPanel;
QColorDialog* m_ColorDialog;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,138 +1,138 @@
/****************************************************************************
**
** This file is part of a Qt Solutions component.
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Solutions Commercial License Agreement provided
** with the Software or, alternatively, in accordance with the terms
** contained in a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** Please note Third Party Software included with Qt Solutions may impose
** additional restrictions and it is the user's responsibility to ensure
** that they have met the licensing requirements of the GPL, LGPL, or Qt
** Solutions Commercial license and the relevant license of the Third
** Party Software they are using.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
****************************************************************************/
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// DoubleColor, Vertex and ColorTriangle classes.
/// </summary>
/// <summary>
/// Used to store color values in the range 0..255 as doubles.
/// </summary>
struct DoubleColor
{
double r, g, b;
DoubleColor() : r(0.0), g(0.0), b(0.0) {}
DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
DoubleColor(const DoubleColor& c) : r(c.r), g(c.g), b(c.b) {}
};
/// <summary>
/// Used to store pairs of DoubleColor and DoublePoint in one structure.
/// </summary>
struct Vertex
{
DoubleColor color;
QPointF point;
Vertex(const DoubleColor& c, const QPointF& p) : color(c), point(p) {}
Vertex(const QColor& c, const QPointF& p)
: color(DoubleColor(static_cast<double>(c.red()), static_cast<double>(c.green()),
static_cast<double>(c.blue()))), point(p) {}
};
/// <summary>
/// Widget for drawing a color triangle which allows users to select colors
/// in a manner more intuitive than the usual color picker dialog.
/// This class was taken from an open source project named Chaos Helper, which took
/// it from the Qt examples, so it mostly remains as-is.
/// </summary>
class ColorTriangle : public QWidget
{
Q_OBJECT
public:
ColorTriangle(QWidget* parent = nullptr);
void Polish();
QColor Color() const;
void Color(const QColor& col);
int heightForWidth(int w) const override;
QSize sizeHint() const override;
Q_SIGNALS:
void ColorChanged(const QColor& col);
protected:
void paintEvent(QPaintEvent*) override;
void mouseMoveEvent(QMouseEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void keyPressEvent(QKeyEvent* e) override;
void resizeEvent(QResizeEvent*) override;
private:
void GenBackground();
void DrawTrigon(QImage* p, const QPointF& a, const QPointF& b, const QPointF& c, const QColor& color);
double CalcOuterRadius() const;
double RadiusAt(const QPointF& pos, const QRect& rect) const;
double AngleAt(const QPointF& pos, const QRect& rect) const;
QColor ColorFromPoint(const QPointF& p) const;
QPointF PointFromColor(const QColor& col) const;
QPointF MovePointToTriangle(double x, double y, const Vertex& a, const Vertex& b, const Vertex& c) const;
bool mustGenerateBackground;
int curHue;
int penWidth;
int ellipseSize;
int outerRadius;
double a, b, c;
QImage bg;
QColor curColor;
QPointF pa, pb, pc, pd;
QPointF selectorPos;
enum SelectionMode
{
Idle,
SelectingHue,
SelectingSatValue
} selMode;
};
/****************************************************************************
**
** This file is part of a Qt Solutions component.
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Solutions Commercial License Agreement provided
** with the Software or, alternatively, in accordance with the terms
** contained in a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** Please note Third Party Software included with Qt Solutions may impose
** additional restrictions and it is the user's responsibility to ensure
** that they have met the licensing requirements of the GPL, LGPL, or Qt
** Solutions Commercial license and the relevant license of the Third
** Party Software they are using.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
****************************************************************************/
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// DoubleColor, Vertex and ColorTriangle classes.
/// </summary>
/// <summary>
/// Used to store color values in the range 0..255 as doubles.
/// </summary>
struct DoubleColor
{
double r, g, b;
DoubleColor() : r(0.0), g(0.0), b(0.0) {}
DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
DoubleColor(const DoubleColor& c) : r(c.r), g(c.g), b(c.b) {}
};
/// <summary>
/// Used to store pairs of DoubleColor and DoublePoint in one structure.
/// </summary>
struct Vertex
{
DoubleColor color;
QPointF point;
Vertex(const DoubleColor& c, const QPointF& p) : color(c), point(p) {}
Vertex(const QColor& c, const QPointF& p)
: color(DoubleColor(static_cast<double>(c.red()), static_cast<double>(c.green()),
static_cast<double>(c.blue()))), point(p) {}
};
/// <summary>
/// Widget for drawing a color triangle which allows users to select colors
/// in a manner more intuitive than the usual color picker dialog.
/// This class was taken from an open source project named Chaos Helper, which took
/// it from the Qt examples, so it mostly remains as-is.
/// </summary>
class ColorTriangle : public QWidget
{
Q_OBJECT
public:
ColorTriangle(QWidget* parent = nullptr);
void Polish();
QColor Color() const;
void Color(const QColor& col);
int heightForWidth(int w) const override;
QSize sizeHint() const override;
Q_SIGNALS:
void ColorChanged(const QColor& col);
protected:
void paintEvent(QPaintEvent*) override;
void mouseMoveEvent(QMouseEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void keyPressEvent(QKeyEvent* e) override;
void resizeEvent(QResizeEvent*) override;
private:
void GenBackground();
void DrawTrigon(QImage* p, const QPointF& a, const QPointF& b, const QPointF& c, const QColor& color);
double CalcOuterRadius() const;
double RadiusAt(const QPointF& pos, const QRect& rect) const;
double AngleAt(const QPointF& pos, const QRect& rect) const;
QColor ColorFromPoint(const QPointF& p) const;
QPointF PointFromColor(const QColor& col) const;
QPointF MovePointToTriangle(double x, double y, const Vertex& a, const Vertex& b, const Vertex& c) const;
bool mustGenerateBackground;
int curHue;
int penWidth;
int ellipseSize;
int outerRadius;
double a, b, c;
QImage bg;
QColor curColor;
QPointF pa, pb, pc, pd;
QPointF selectorPos;
enum SelectionMode
{
Idle,
SelectingHue,
SelectingSatValue
} selMode;
};

View File

@ -1,127 +1,127 @@
/****************************************************************************/
// This file is part of the gradLib library originally made by Stian Broen
//
// For more free libraries, please visit <http://broentech.no>
//
// gradLib is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library. If not, see <http://www.gnu.org/licenses/>
/****************************************************************************/
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// Class for drawing the small arrows below the gradient in the palette editor.
/// The drawing is accomplished via a QPolygon object.
/// </summary>
class GradientArrow
{
public:
/// <summary>
/// Default constructor which sets up the size of the arrow.
/// </summary>
explicit GradientArrow()
{
QPolygon area;
area << QPoint(5, 5) << QPoint(10, 0) << QPoint(15, 5) << QPoint(15, 15) << QPoint(5, 15) << QPoint(5, 5);
Area(area);
}
/// <summary>
/// Constructor which takes the color and focus state of the arrow.
/// </summary>
/// <param name="col">The color of the arrow</param>
/// <param name="focus">Whether the arrow is focused</param>
explicit GradientArrow(QColor col, bool focus)
: GradientArrow()
{
m_Color = col;
m_Focus = focus;
}
/// <summary>
/// Copy constructor to copy another GradientArrow.
/// </summary>
/// <param name="other">The GradientArrow object to copy</param>
GradientArrow(const GradientArrow& other)
: m_Focus(other.Focus()),
m_Area(other.Area()),
m_Color(other.Color())
{
}
/// <summary>
/// Getters and setters.
/// </summary>
inline bool Focus() const { return m_Focus; }
inline void Focus(bool val) { m_Focus = val; }
inline const QPolygon Area() const { return m_Area; }
inline void Area(const QPolygon& val) {m_Area = val; }
inline const QColor Color() const { return m_Color; }
inline void Color(const QColor& val) { m_Color = val; }
private:
bool m_Focus;
QPolygon m_Area;
QColor m_Color;
};
/// <summary>
/// Thin derivation to handle drawing arrows at the top of the gradient area to
/// represent the color indices of each xform.
/// </summary>
class TopArrow : public GradientArrow
{
public:
/// <summary>
/// Default constructor which is only present so this class can be used with containers.
/// This should never be used by a caller.
/// </summary>
TopArrow()
: TopArrow(10, 0)
{
}
/// <summary>
/// Constructor which takes the width used to draw the arrow and the xform index
/// this arrow represents.
/// </summary>
/// <param name="width">The width used to draw the arrow</param>
/// <param name="index">The xform index this arrow represents</param>
TopArrow(int width, size_t index)
{
QPolygon area;
constexpr int center = 10;
const auto mid = width / 2;
const auto left = center - mid;
const auto right = center + mid;
area << QPoint(left, 0) << QPoint(right, 0) << QPoint(right, 10) << QPoint(center, 15) << QPoint(left, 10) << QPoint(left, 0);
Area(area);
m_Index = index;
m_Width = width;
m_Text = QString::number(index + 1);
}
/// <summary>
/// Getters.
/// </summary>
int Width() { return m_Width; }
size_t Index() { return m_Index; }
QString Text() { return m_Text; }
private:
int m_Width;
size_t m_Index;
QString m_Text;
/****************************************************************************/
// This file is part of the gradLib library originally made by Stian Broen
//
// For more free libraries, please visit <http://broentech.no>
//
// gradLib is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library. If not, see <http://www.gnu.org/licenses/>
/****************************************************************************/
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// Class for drawing the small arrows below the gradient in the palette editor.
/// The drawing is accomplished via a QPolygon object.
/// </summary>
class GradientArrow
{
public:
/// <summary>
/// Default constructor which sets up the size of the arrow.
/// </summary>
explicit GradientArrow()
{
QPolygon area;
area << QPoint(5, 5) << QPoint(10, 0) << QPoint(15, 5) << QPoint(15, 15) << QPoint(5, 15) << QPoint(5, 5);
Area(area);
}
/// <summary>
/// Constructor which takes the color and focus state of the arrow.
/// </summary>
/// <param name="col">The color of the arrow</param>
/// <param name="focus">Whether the arrow is focused</param>
explicit GradientArrow(QColor col, bool focus)
: GradientArrow()
{
m_Color = col;
m_Focus = focus;
}
/// <summary>
/// Copy constructor to copy another GradientArrow.
/// </summary>
/// <param name="other">The GradientArrow object to copy</param>
GradientArrow(const GradientArrow& other)
: m_Focus(other.Focus()),
m_Area(other.Area()),
m_Color(other.Color())
{
}
/// <summary>
/// Getters and setters.
/// </summary>
inline bool Focus() const { return m_Focus; }
inline void Focus(bool val) { m_Focus = val; }
inline const QPolygon Area() const { return m_Area; }
inline void Area(const QPolygon& val) {m_Area = val; }
inline const QColor Color() const { return m_Color; }
inline void Color(const QColor& val) { m_Color = val; }
private:
bool m_Focus;
QPolygon m_Area;
QColor m_Color;
};
/// <summary>
/// Thin derivation to handle drawing arrows at the top of the gradient area to
/// represent the color indices of each xform.
/// </summary>
class TopArrow : public GradientArrow
{
public:
/// <summary>
/// Default constructor which is only present so this class can be used with containers.
/// This should never be used by a caller.
/// </summary>
TopArrow()
: TopArrow(10, 0)
{
}
/// <summary>
/// Constructor which takes the width used to draw the arrow and the xform index
/// this arrow represents.
/// </summary>
/// <param name="width">The width used to draw the arrow</param>
/// <param name="index">The xform index this arrow represents</param>
TopArrow(int width, size_t index)
{
QPolygon area;
constexpr int center = 10;
const auto mid = width / 2;
const auto left = center - mid;
const auto right = center + mid;
area << QPoint(left, 0) << QPoint(right, 0) << QPoint(right, 10) << QPoint(center, 15) << QPoint(left, 10) << QPoint(left, 0);
Area(area);
m_Index = index;
m_Width = width;
m_Text = QString::number(index + 1);
}
/// <summary>
/// Getters.
/// </summary>
int Width() { return m_Width; }
size_t Index() { return m_Index; }
QString Text() { return m_Text; }
private:
int m_Width;
size_t m_Index;
QString m_Text;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,89 +1,89 @@
/****************************************************************************/
// This file is part of the gradLib library originally made by Stian Broen
//
// For more free libraries, please visit <http://broentech.no>
//
// gradLib is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library. If not, see <http://www.gnu.org/licenses/>
/****************************************************************************/
#pragma once
#include "FractoriumPch.h"
#include "GradientArrow.h"
/// <summary>
/// Class for drawing the resulting palette created by interpolating the key colors
/// as well as the arrows underneath the palette.
/// The arrows are held in a sorted map whose key is the normalized index of the arrow,
/// between 0 and 1 inclusive. They value is the arrow itself.
/// The resulting palette is always stored in the m_Palette member.
/// </summary>
class GradientColorsView : public QWidget
{
Q_OBJECT
public:
explicit GradientColorsView(QWidget* p = nullptr);
bool Blend();
void Blend(bool blend);
void SetFocus(float position);
void SetFocus(size_t position);
void SetFocusColor(const QColor& col);
void AddArrow(const QColor& color);
void AddArrow(float position, const QColor& color);
void DeleteFocusedArrow();
void InvertColors();
void RandomColors();
void DistributeColors();
void ResetToDefault();
void ClearArrows();
void NewFocusColor(const QColor& col, int index);
void SetArrows(map<float, GradientArrow>& newArrows);
int ArrowCount();
int GetFocusedIndex();
map<float, GradientArrow>& GetArrows();
Palette<float>& GetPalette(int size);
void SetPalette(const Palette<float>& palette);
map<size_t, float> GetColorIndices() const;
void SetColorIndices(const map<size_t, float>& indices);
Q_SIGNALS:
void ArrowMove(qreal lastPos, const GradientArrow& arrow);
void ArrowDoubleClicked(const GradientArrow& arrow);
void ColorIndexMove(size_t index, float value);
protected:
void paintEvent(QPaintEvent* e) override;
void mousePressEvent(QMouseEvent* e) override;
void mouseDoubleClickEvent(QMouseEvent* e) override;
void mouseMoveEvent(QMouseEvent* e) override;
void mouseReleaseEvent(QMouseEvent* e) override;
void resizeEvent(QResizeEvent*) override;
private:
int RectWidth();
int RectHeight();
bool m_ArrowMoving = false;
bool m_ColorIndexArrowMoving = false;
bool m_Blend = true;
QPoint m_ViewRectSize;
QPoint m_ViewRectOffset = QPoint(5, 15);
QPoint m_ViewRectTranslate = QPoint(5, 5);
QRect m_ViewRect;
QPoint m_DragStart;
map<float, GradientArrow> m_Arrows;
map<size_t, pair<float, TopArrow>> m_ColorIndicesArrows;
Palette<float> m_Palette;
QPixmap m_FinalFixedPixmap;
};
/****************************************************************************/
// This file is part of the gradLib library originally made by Stian Broen
//
// For more free libraries, please visit <http://broentech.no>
//
// gradLib is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library. If not, see <http://www.gnu.org/licenses/>
/****************************************************************************/
#pragma once
#include "FractoriumPch.h"
#include "GradientArrow.h"
/// <summary>
/// Class for drawing the resulting palette created by interpolating the key colors
/// as well as the arrows underneath the palette.
/// The arrows are held in a sorted map whose key is the normalized index of the arrow,
/// between 0 and 1 inclusive. They value is the arrow itself.
/// The resulting palette is always stored in the m_Palette member.
/// </summary>
class GradientColorsView : public QWidget
{
Q_OBJECT
public:
explicit GradientColorsView(QWidget* p = nullptr);
bool Blend();
void Blend(bool blend);
void SetFocus(float position);
void SetFocus(size_t position);
void SetFocusColor(const QColor& col);
void AddArrow(const QColor& color);
void AddArrow(float position, const QColor& color);
void DeleteFocusedArrow();
void InvertColors();
void RandomColors();
void DistributeColors();
void ResetToDefault();
void ClearArrows();
void NewFocusColor(const QColor& col, int index);
void SetArrows(map<float, GradientArrow>& newArrows);
int ArrowCount();
int GetFocusedIndex();
map<float, GradientArrow>& GetArrows();
Palette<float>& GetPalette(int size);
void SetPalette(const Palette<float>& palette);
map<size_t, float> GetColorIndices() const;
void SetColorIndices(const map<size_t, float>& indices);
Q_SIGNALS:
void ArrowMove(qreal lastPos, const GradientArrow& arrow);
void ArrowDoubleClicked(const GradientArrow& arrow);
void ColorIndexMove(size_t index, float value);
protected:
void paintEvent(QPaintEvent* e) override;
void mousePressEvent(QMouseEvent* e) override;
void mouseDoubleClickEvent(QMouseEvent* e) override;
void mouseMoveEvent(QMouseEvent* e) override;
void mouseReleaseEvent(QMouseEvent* e) override;
void resizeEvent(QResizeEvent*) override;
private:
int RectWidth();
int RectHeight();
bool m_ArrowMoving = false;
bool m_ColorIndexArrowMoving = false;
bool m_Blend = true;
QPoint m_ViewRectSize;
QPoint m_ViewRectOffset = QPoint(5, 15);
QPoint m_ViewRectTranslate = QPoint(5, 5);
QRect m_ViewRect;
QPoint m_DragStart;
map<float, GradientArrow> m_Arrows;
map<size_t, pair<float, TopArrow>> m_ColorIndicesArrows;
Palette<float> m_Palette;
QPixmap m_FinalFixedPixmap;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,93 +1,93 @@
#pragma once
#include "FractoriumPch.h"
#include "Fractorium.h"
#include "ColorPickerWidget.h"
#include "GradientColorsView.h"
#include "EmberFile.h"
#include "ui_PaletteEditor.h"
namespace Ui
{
class PaletteEditor;
}
/// <summary>
/// Dialog for editing user created palettes.
/// This will load with all available user created palettes populated in the combo
/// box. As the user changes the selected index, the palettes for that file
/// are shown in the list box. The user can click on those and then edit them and either
/// save it back to the same position in the file, or append it to the end.
/// They can also click in the name column to set/rename the palette name.
/// Any changes on this dialog can be "synced". That is, when the Sync checkbox is checked
/// any changes result in a signal being sent back to the main window.
/// </summary>
class PaletteEditor : public QDialog
{
Q_OBJECT
public:
explicit PaletteEditor(QWidget* p = nullptr);
public:
bool Sync();
Palette<float>& GetPalette(int size);
void SetPalette(const Palette<float>& palette);
map<size_t, float> GetColorIndices() const;
map<size_t, float> GetPreviousColorIndices() const;
void SetColorIndices(const map<size_t, float>& indices);
void SetPreviousColorIndices(const map<size_t, float>& indices);
string GetPaletteFile() const;
void SetPaletteFile(const string& filename);
Q_SIGNALS:
void PaletteChanged();
void PaletteFileChanged();
void ColorIndexChanged(size_t index, float value);
private Q_SLOTS:
void OnAddColorButtonClicked();
void OnRemoveColorButtonClicked();
void OnInvertColorsButtonClicked();
void OnRandomColorsButtonClicked();
void OnDistributeColorsButtonClicked();
void OnResetToDefaultButtonClicked();
void OnCreatePaletteFromImageButtonClicked();
void OnCreatePaletteAgainFromImageButton();
void OnColorPickerColorChanged(const QColor& col);
void OnArrowDoubleClicked(const GradientArrow& arrow);
void OnSyncCheckBoxStateChanged(int state);
void OnBlendCheckBoxStateChanged(int state);
void OnArrowMoved(qreal lastPos, const GradientArrow& arrow);
void OnColorIndexMove(size_t index, float value);
void OnPaletteFilenameComboChanged(const QString& text);
void OnPaletteCellClicked(int row, int col);
void OnPaletteCellChanged(int row, int col);
void OnNewPaletteFileButtonClicked();
void OnCopyPaletteFileButtonClicked();
void OnAppendPaletteButtonClicked();
void OnOverwritePaletteButtonClicked();
void OnDeletePaletteButtonClicked();
private:
void EmitPaletteChanged();
void EmitColorIndexChanged(size_t index, float value);
QStringList SetupOpenImagesDialog();
void AddArrow(const QColor& color);
map<float, GradientArrow> GetRandomColorsFromImage(QString filename, int numPoints);
void EnablePaletteFileControls();
void EnablePaletteControls();
bool IsCurrentPaletteAndFileEditable();
bool m_PaletteFileChanged = false;
int m_PaletteIndex = 0;
map<size_t, float> m_PreviousColorIndices;
QString m_Filename;
string m_CurrentPaletteFilePath;
ColorPickerWidget* m_ColorPicker = nullptr;
GradientColorsView* m_GradientColorView = nullptr;
#ifndef __APPLE__
QFileDialog* m_FileDialog = nullptr;
#endif
shared_ptr<PaletteList<float>> m_PaletteList;
std::unique_ptr<Ui::PaletteEditor> ui;
};
#pragma once
#include "FractoriumPch.h"
#include "Fractorium.h"
#include "ColorPickerWidget.h"
#include "GradientColorsView.h"
#include "EmberFile.h"
#include "ui_PaletteEditor.h"
namespace Ui
{
class PaletteEditor;
}
/// <summary>
/// Dialog for editing user created palettes.
/// This will load with all available user created palettes populated in the combo
/// box. As the user changes the selected index, the palettes for that file
/// are shown in the list box. The user can click on those and then edit them and either
/// save it back to the same position in the file, or append it to the end.
/// They can also click in the name column to set/rename the palette name.
/// Any changes on this dialog can be "synced". That is, when the Sync checkbox is checked
/// any changes result in a signal being sent back to the main window.
/// </summary>
class PaletteEditor : public QDialog
{
Q_OBJECT
public:
explicit PaletteEditor(QWidget* p = nullptr);
public:
bool Sync();
Palette<float>& GetPalette(int size);
void SetPalette(const Palette<float>& palette);
map<size_t, float> GetColorIndices() const;
map<size_t, float> GetPreviousColorIndices() const;
void SetColorIndices(const map<size_t, float>& indices);
void SetPreviousColorIndices(const map<size_t, float>& indices);
string GetPaletteFile() const;
void SetPaletteFile(const string& filename);
Q_SIGNALS:
void PaletteChanged();
void PaletteFileChanged();
void ColorIndexChanged(size_t index, float value);
private Q_SLOTS:
void OnAddColorButtonClicked();
void OnRemoveColorButtonClicked();
void OnInvertColorsButtonClicked();
void OnRandomColorsButtonClicked();
void OnDistributeColorsButtonClicked();
void OnResetToDefaultButtonClicked();
void OnCreatePaletteFromImageButtonClicked();
void OnCreatePaletteAgainFromImageButton();
void OnColorPickerColorChanged(const QColor& col);
void OnArrowDoubleClicked(const GradientArrow& arrow);
void OnSyncCheckBoxStateChanged(int state);
void OnBlendCheckBoxStateChanged(int state);
void OnArrowMoved(qreal lastPos, const GradientArrow& arrow);
void OnColorIndexMove(size_t index, float value);
void OnPaletteFilenameComboChanged(const QString& text);
void OnPaletteCellClicked(int row, int col);
void OnPaletteCellChanged(int row, int col);
void OnNewPaletteFileButtonClicked();
void OnCopyPaletteFileButtonClicked();
void OnAppendPaletteButtonClicked();
void OnOverwritePaletteButtonClicked();
void OnDeletePaletteButtonClicked();
private:
void EmitPaletteChanged();
void EmitColorIndexChanged(size_t index, float value);
QStringList SetupOpenImagesDialog();
void AddArrow(const QColor& color);
map<float, GradientArrow> GetRandomColorsFromImage(QString filename, int numPoints);
void EnablePaletteFileControls();
void EnablePaletteControls();
bool IsCurrentPaletteAndFileEditable();
bool m_PaletteFileChanged = false;
int m_PaletteIndex = 0;
map<size_t, float> m_PreviousColorIndices;
QString m_Filename;
string m_CurrentPaletteFilePath;
ColorPickerWidget* m_ColorPicker = nullptr;
GradientColorsView* m_GradientColorView = nullptr;
#ifndef __APPLE__
QFileDialog* m_FileDialog = nullptr;
#endif
shared_ptr<PaletteList<float>> m_PaletteList;
std::unique_ptr<Ui::PaletteEditor> ui;
};

View File

@ -1,27 +1,27 @@
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// PaletteTableWidgetItem class.
/// </summary>
/// <summary>
/// A thin derivation of QTableWidgetItem which keeps a pointer to a palette object.
/// The lifetime of the palette object must be greater than or equal to
/// the lifetime of this object.
/// </summary>
class PaletteTableWidgetItem : public QTableWidgetItem
{
public:
PaletteTableWidgetItem(Palette<float>* palette)
: m_Palette(palette)
{
}
size_t Index() const { return m_Palette->m_Index; }
Palette<float>* GetPalette() const { return m_Palette; }
private:
Palette<float>* m_Palette;
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// PaletteTableWidgetItem class.
/// </summary>
/// <summary>
/// A thin derivation of QTableWidgetItem which keeps a pointer to a palette object.
/// The lifetime of the palette object must be greater than or equal to
/// the lifetime of this object.
/// </summary>
class PaletteTableWidgetItem : public QTableWidgetItem
{
public:
PaletteTableWidgetItem(Palette<float>* palette)
: m_Palette(palette)
{
}
size_t Index() const { return m_Palette->m_Index; }
Palette<float>* GetPalette() const { return m_Palette; }
private:
Palette<float>* m_Palette;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,143 +1,143 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include "Fractorium.h"
#include "FractoriumCommon.h"
#include "QssTextEdit.h"
#include "qcssparser.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
namespace Ui
{
class QssDialog;
}
/// <summary>
/// A dialog for editing the stylesheet used in the application.
/// This is meant to be used in the following way:
/// On first run, no stylesheet is present/selected, so a basic style
/// is used on startup. This style differs slightly between Windows and Linux. See BaseStyle() for details.
/// If the user clicks Save as default or ok to exit the dialog, the text of this stylesheet will
/// be saved to the application settings folder in the file default.qss.
/// On all subsequent runs, the main window will detect the presence of default.qss and load it.
/// The user can load a different stylesheet from disk, such as dark.qss which comes with the installation.
/// They can save this back to disk (under a different name because dark.qss is made read only by the installer),
/// however it will not become the default until they explicitly click the Save as default button or click ok.
/// The other buttons Basic, Medium and Advanced produce an empty style sheet that gives access to various controls.
/// Basic: Just the base style.
/// Medium: Basic + every type of control in the application.
/// Advanced: Medium + the name of every individual control in the application. It is not intended that the user fill
/// out a custom style for every single control. Rather, it's to make them aware of the names of the controls in the
/// event they want to set some custom styling for a specific control.
/// For all practical purposes, the user will probably start with dark.qss, edit what they need and save to a new stylesheet,
// then set that one as the default.
/// </summary>
class QssDialog : public QDialog
{
Q_OBJECT
public:
explicit QssDialog(Fractorium* parent);
~QssDialog();
QString Text() const;
void SetText(const QString& t);
QList<QString> GetClassNames(bool includeObjectNames);
static bool IsStyleSheetValid(const QString& styleSheet);
public slots:
virtual void accept() override;
virtual void reject() override;
protected:
virtual void showEvent(QShowEvent* e) override;
private slots:
void SlotTextChanged();
void SlotAddColor(const QString& p);
void SlotAddGeom(const QString& p);
void SlotAddBorder(const QString& p);
void SlotSetTheme(const QString& s);
void SlotAddFont();
void SlotApplyCss();
void LoadButton_clicked();
void SaveButton_clicked();
void BasicButton_clicked();
void MediumButton_clicked();
void AdvancedButton_clicked();
private:
void SaveAsDefault();
void InsertCssProperty(const QString& name, const QString& value);
void SetupFileDialog();
QString OpenFile();
QString SaveFile();
QStyle* m_Theme = nullptr;
QStyle* m_LastTheme;
QString m_LastStyle;
QAction* m_AddColorAction;
QAction* m_AddGeomAction;
QAction* m_AddBorderAction;
QAction* m_AddFontAction;
QAction* m_AddStyleAction;
QSignalMapper* m_ColorActionMapper;
QSignalMapper* m_GeomActionMapper;
QSignalMapper* m_BorderActionMapper;
QSignalMapper* m_StyleActionMapper;
QHash<QString, QString> m_ColorMap;
QHash<QString, QString> m_GeomMap;
QHash<QString, QString> m_BorderMap;
QHash<QString, QString> m_StyleMap;
QTimer* m_ApplyTimer;
Fractorium* m_Parent;
#ifndef __APPLE__
QFileDialog* m_FileDialog = nullptr;
#endif
Ui::QssDialog* ui;
};
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include "Fractorium.h"
#include "FractoriumCommon.h"
#include "QssTextEdit.h"
#include "qcssparser.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
namespace Ui
{
class QssDialog;
}
/// <summary>
/// A dialog for editing the stylesheet used in the application.
/// This is meant to be used in the following way:
/// On first run, no stylesheet is present/selected, so a basic style
/// is used on startup. This style differs slightly between Windows and Linux. See BaseStyle() for details.
/// If the user clicks Save as default or ok to exit the dialog, the text of this stylesheet will
/// be saved to the application settings folder in the file default.qss.
/// On all subsequent runs, the main window will detect the presence of default.qss and load it.
/// The user can load a different stylesheet from disk, such as dark.qss which comes with the installation.
/// They can save this back to disk (under a different name because dark.qss is made read only by the installer),
/// however it will not become the default until they explicitly click the Save as default button or click ok.
/// The other buttons Basic, Medium and Advanced produce an empty style sheet that gives access to various controls.
/// Basic: Just the base style.
/// Medium: Basic + every type of control in the application.
/// Advanced: Medium + the name of every individual control in the application. It is not intended that the user fill
/// out a custom style for every single control. Rather, it's to make them aware of the names of the controls in the
/// event they want to set some custom styling for a specific control.
/// For all practical purposes, the user will probably start with dark.qss, edit what they need and save to a new stylesheet,
// then set that one as the default.
/// </summary>
class QssDialog : public QDialog
{
Q_OBJECT
public:
explicit QssDialog(Fractorium* parent);
~QssDialog();
QString Text() const;
void SetText(const QString& t);
QList<QString> GetClassNames(bool includeObjectNames);
static bool IsStyleSheetValid(const QString& styleSheet);
public slots:
virtual void accept() override;
virtual void reject() override;
protected:
virtual void showEvent(QShowEvent* e) override;
private slots:
void SlotTextChanged();
void SlotAddColor(const QString& p);
void SlotAddGeom(const QString& p);
void SlotAddBorder(const QString& p);
void SlotSetTheme(const QString& s);
void SlotAddFont();
void SlotApplyCss();
void LoadButton_clicked();
void SaveButton_clicked();
void BasicButton_clicked();
void MediumButton_clicked();
void AdvancedButton_clicked();
private:
void SaveAsDefault();
void InsertCssProperty(const QString& name, const QString& value);
void SetupFileDialog();
QString OpenFile();
QString SaveFile();
QStyle* m_Theme = nullptr;
QStyle* m_LastTheme;
QString m_LastStyle;
QAction* m_AddColorAction;
QAction* m_AddGeomAction;
QAction* m_AddBorderAction;
QAction* m_AddFontAction;
QAction* m_AddStyleAction;
QSignalMapper* m_ColorActionMapper;
QSignalMapper* m_GeomActionMapper;
QSignalMapper* m_BorderActionMapper;
QSignalMapper* m_StyleActionMapper;
QHash<QString, QString> m_ColorMap;
QHash<QString, QString> m_GeomMap;
QHash<QString, QString> m_BorderMap;
QHash<QString, QString> m_StyleMap;
QTimer* m_ApplyTimer;
Fractorium* m_Parent;
#ifndef __APPLE__
QFileDialog* m_FileDialog = nullptr;
#endif
Ui::QssDialog* ui;
};

View File

@ -1,153 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QssDialog</class>
<widget class="QDialog" name="QssDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1041</width>
<height>644</height>
</rect>
</property>
<property name="windowTitle">
<string>QSS Editor</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="QHBoxLayout" name="QssHorizontalLayout">
<item>
<widget class="QssTextEdit" name="QssEdit"/>
</item>
<item>
<layout class="QVBoxLayout" name="QssVerticalLayout">
<item>
<widget class="QPushButton" name="QssLoadButton">
<property name="text">
<string>Load...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssSaveButton">
<property name="text">
<string>Save...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssBasicButton">
<property name="text">
<string>Basic</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssMediumButton">
<property name="text">
<string>Medium</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssAdvancedButton">
<property name="text">
<string>Advanced</string>
</property>
</widget>
</item>
<item>
<spacer name="QssVerticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="QssButtonBoxHorizontalLayout">
<item>
<widget class="QLabel" name="QssValidityLabel">
<property name="text">
<string>Valid Qss</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="QssButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QssTextEdit</class>
<extends>QTextEdit</extends>
<header>QssTextEdit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>QssButtonBox</sender>
<signal>accepted()</signal>
<receiver>QssDialog</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>QssButtonBox</sender>
<signal>rejected()</signal>
<receiver>QssDialog</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>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QssDialog</class>
<widget class="QDialog" name="QssDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1041</width>
<height>644</height>
</rect>
</property>
<property name="windowTitle">
<string>QSS Editor</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="QHBoxLayout" name="QssHorizontalLayout">
<item>
<widget class="QssTextEdit" name="QssEdit"/>
</item>
<item>
<layout class="QVBoxLayout" name="QssVerticalLayout">
<item>
<widget class="QPushButton" name="QssLoadButton">
<property name="text">
<string>Load...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssSaveButton">
<property name="text">
<string>Save...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssBasicButton">
<property name="text">
<string>Basic</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssMediumButton">
<property name="text">
<string>Medium</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssAdvancedButton">
<property name="text">
<string>Advanced</string>
</property>
</widget>
</item>
<item>
<spacer name="QssVerticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="QssButtonBoxHorizontalLayout">
<item>
<widget class="QLabel" name="QssValidityLabel">
<property name="text">
<string>Valid Qss</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="QssButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QssTextEdit</class>
<extends>QTextEdit</extends>
<header>QssTextEdit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>QssButtonBox</sender>
<signal>accepted()</signal>
<receiver>QssDialog</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>QssButtonBox</sender>
<signal>rejected()</signal>
<receiver>QssDialog</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>

View File

@ -1,10 +1,10 @@
#include "FractoriumPch.h"
#include "QssTextEdit.h"
QssTextEdit::QssTextEdit(QWidget* parent) :
QTextEdit(parent)
{
setTabStopWidth(fontMetrics().width(QLatin1Char(' '))*4);
setAcceptRichText(false);
new CssHighlighter(document());
}
#include "FractoriumPch.h"
#include "QssTextEdit.h"
QssTextEdit::QssTextEdit(QWidget* parent) :
QTextEdit(parent)
{
setTabStopDistance(fontMetrics().horizontalAdvance(QLatin1Char(' ')) * 4);
setAcceptRichText(false);
new CssHighlighter(document());
}

View File

@ -1,56 +1,56 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include "FractoriumPch.h"
#include "csshighlighter.h"
class QssTextEdit : public QTextEdit
{
Q_OBJECT
public:
QssTextEdit(QWidget* parent = nullptr);
signals:
public slots:
};
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include "FractoriumPch.h"
#include "csshighlighter.h"
class QssTextEdit : public QTextEdit
{
Q_OBJECT
public:
QssTextEdit(QWidget* parent = nullptr);
signals:
public slots:
};

View File

@ -1,388 +1,388 @@
#include "FractoriumPch.h"
#include "SpinBox.h"
#include "FractoriumSettings.h"
QTimer SpinBox::s_Timer;
/// <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="p">The parent widget. Default: nullptr.</param>
/// <param name="h">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* p, int h, int step)
: QSpinBox(p)
{
m_DoubleClick = false;
m_DoubleClickLowVal = 0;
m_DoubleClickNonZero = 0;
m_DoubleClickZero = 1;
m_Step = step;
m_SmallStep = 1;
m_Settings = FractoriumSettings::DefInstance();
setSingleStep(step);
setFrame(false);
setButtonSymbols(QAbstractSpinBox::NoButtons);
setFocusPolicy(Qt::StrongFocus);
setMinimumHeight(h);//setGeometry() has no effect, so set both of these instead.
setMaximumHeight(h);
setContextMenuPolicy(Qt::PreventContextMenu);
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);
}
void SpinBox::SetValueStealth(size_t d) { SetValueStealth(static_cast<int>(d)); }
/// <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 instead of zero to represent the lower value
/// used when responding to a double click.
/// </summary>
/// <param name="val">The value to be used for the lower value instead of zero</param>
void SpinBox::DoubleClickLowVal(int val)
{
m_DoubleClickLowVal = val;
}
int SpinBox::DoubleClickLowVal()
{
return m_DoubleClickLowVal;
}
/// <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;
}
int SpinBox::DoubleClickZero()
{
return m_DoubleClickZero;
}
/// <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;
}
int SpinBox::DoubleClickNonZero()
{
return m_DoubleClickNonZero;
}
/// <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 = std::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>
/// Called while the timer is activated due to the right mouse button being held down.
/// </summary>
void SpinBox::OnTimeout()
{
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
distance = Sqr(distance) * (distance < 0 ? -1 : 1);
double scale, val;
int d = value();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
double amount = (m_SmallStep + m_Step) * 0.5;
if (shift)
scale = 0.001;
else if (ctrl)
scale = 0.01;
else
scale = 0.01;
val = d + (distance * amount * scale);
setValue(int(val));
}
/// <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 (!isEnabled())
return QSpinBox::eventFilter(o, e);
const auto me = dynamic_cast<QMouseEvent*>(e);
if (me)
{
if (!m_Settings->ToggleType() &&//Ensure double click toggles, not right click.
me->type() == QMouseEvent::MouseButtonPress &&
me->button() == Qt::RightButton)
{
m_MouseDownPoint = m_MouseMovePoint = me->pos();
StartTimer();
e->accept();
return true;
}
else if (!m_Settings->ToggleType() &&
me->type() == QMouseEvent::MouseButtonRelease &&
me->button() == Qt::RightButton)
{
StopTimer();
m_MouseDownPoint = m_MouseMovePoint = me->pos();
e->accept();
return true;
}
else if (!m_Settings->ToggleType() &&
me->type() == QMouseEvent::MouseMove &&
QGuiApplication::mouseButtons() & Qt::RightButton)
{
m_MouseMovePoint = me->pos();
e->accept();
return true;
}
else if (m_DoubleClick &&
((!m_Settings->ToggleType() && e->type() == QMouseEvent::MouseButtonDblClick && me->button() == Qt::LeftButton) ||
(m_Settings->ToggleType() && me->type() == QMouseEvent::MouseButtonRelease && me->button() == Qt::RightButton)))
{
if (m_DoubleClickLowVal == value())
{
m_DoubleClickZeroEvent(this, m_DoubleClickZero);
setValue(m_DoubleClickZero);
}
else
{
m_DoubleClickNonZeroEvent(this, m_DoubleClickNonZero);
setValue(m_DoubleClickNonZero);
}
e->accept();
return true;
}
}
else if (isEnabled())
{
if (e->type() == QEvent::Wheel)
{
if (QWheelEvent* we = dynamic_cast<QWheelEvent*>(e))
{
const auto shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
const auto ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (we->angleDelta().ry() > 0)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() + m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() + (ctrl ? m_Step * 10 : m_Step));
}
}
else
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() - m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() - (ctrl ? m_Step * 10 : m_Step));
}
}
e->accept();
return true;
}
}
else if (dynamic_cast<QKeyEvent*>(e))
{
e->accept();
return true;
}
}
return QSpinBox::eventFilter(o, e);
}
/// <summary>
/// Override which is for handling specific key presses while this control is focused.
/// In particular, + = and up arrow increase the value, equivalent to scrolling the mouse wheel up, while also observing shift/ctrl modifiers.
/// Values are decreased in the same way by pressing - _ or down arrow.
/// </summary>
/// <param name="ke">The key event</param>
void SpinBox::keyPressEvent(QKeyEvent* ke)
{
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (ke->key() == Qt::Key_Up)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() + m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() + (ctrl ? m_Step * 10 : m_Step));
}
ke->accept();
}
else if (ke->key() == Qt::Key_Down)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() - m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() - (ctrl ? m_Step * 10 : m_Step));
}
ke->accept();
}
else if (ke->key() == Qt::Key_Space)
{
if (m_DoubleClickLowVal == value())
{
m_DoubleClickZeroEvent(this, m_DoubleClickZero);
setValue(m_DoubleClickZero);
}
else
{
m_DoubleClickNonZeroEvent(this, m_DoubleClickNonZero);
setValue(m_DoubleClickNonZero);
}
}
else
QSpinBox::keyPressEvent(ke);
}
/// <summary>
/// Called when focus enters the spinner.
/// </summary>
/// <param name="e">The event</param>
void SpinBox::focusInEvent(QFocusEvent* e)
{
StopTimer();
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)
{
StopTimer();
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)
{
StopTimer();
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)
{
StopTimer();
QSpinBox::leaveEvent(e);
}
/// <summary>
/// Start the timer in response to the right mouse button being pressed.
/// </summary>
void SpinBox::StartTimer()
{
s_Timer.stop();
connect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
s_Timer.start(300);
}
/// <summary>
/// Stop the timer in response to the left mouse button being pressed.
/// </summary>
void SpinBox::StopTimer()
{
s_Timer.stop();
disconnect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
}
#include "FractoriumPch.h"
#include "SpinBox.h"
#include "FractoriumSettings.h"
QTimer SpinBox::s_Timer;
/// <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="p">The parent widget. Default: nullptr.</param>
/// <param name="h">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* p, int h, int step)
: QSpinBox(p)
{
m_DoubleClick = false;
m_DoubleClickLowVal = 0;
m_DoubleClickNonZero = 0;
m_DoubleClickZero = 1;
m_Step = step;
m_SmallStep = 1;
m_Settings = FractoriumSettings::DefInstance();
setSingleStep(step);
setFrame(false);
setButtonSymbols(QAbstractSpinBox::NoButtons);
setFocusPolicy(Qt::StrongFocus);
setMinimumHeight(h);//setGeometry() has no effect, so set both of these instead.
setMaximumHeight(h);
setContextMenuPolicy(Qt::PreventContextMenu);
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);
}
void SpinBox::SetValueStealth(size_t d) { SetValueStealth(static_cast<int>(d)); }
/// <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 instead of zero to represent the lower value
/// used when responding to a double click.
/// </summary>
/// <param name="val">The value to be used for the lower value instead of zero</param>
void SpinBox::DoubleClickLowVal(int val)
{
m_DoubleClickLowVal = val;
}
int SpinBox::DoubleClickLowVal()
{
return m_DoubleClickLowVal;
}
/// <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;
}
int SpinBox::DoubleClickZero()
{
return m_DoubleClickZero;
}
/// <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;
}
int SpinBox::DoubleClickNonZero()
{
return m_DoubleClickNonZero;
}
/// <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 = std::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>
/// Called while the timer is activated due to the right mouse button being held down.
/// </summary>
void SpinBox::OnTimeout()
{
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
distance = Sqr(distance) * (distance < 0 ? -1 : 1);
double scale, val;
int d = value();
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
double amount = (m_SmallStep + m_Step) * 0.5;
if (shift)
scale = 0.001;
else if (ctrl)
scale = 0.01;
else
scale = 0.01;
val = d + (distance * amount * scale);
setValue(int(val));
}
/// <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 (!isEnabled())
return QSpinBox::eventFilter(o, e);
const auto me = dynamic_cast<QMouseEvent*>(e);
if (me)
{
if (!m_Settings->ToggleType() &&//Ensure double click toggles, not right click.
me->type() == QMouseEvent::MouseButtonPress &&
me->button() == Qt::RightButton)
{
m_MouseDownPoint = m_MouseMovePoint = me->pos();
StartTimer();
e->accept();
return true;
}
else if (!m_Settings->ToggleType() &&
me->type() == QMouseEvent::MouseButtonRelease &&
me->button() == Qt::RightButton)
{
StopTimer();
m_MouseDownPoint = m_MouseMovePoint = me->pos();
e->accept();
return true;
}
else if (!m_Settings->ToggleType() &&
me->type() == QMouseEvent::MouseMove &&
QGuiApplication::mouseButtons() & Qt::RightButton)
{
m_MouseMovePoint = me->pos();
e->accept();
return true;
}
else if (m_DoubleClick &&
((!m_Settings->ToggleType() && e->type() == QMouseEvent::MouseButtonDblClick && me->button() == Qt::LeftButton) ||
(m_Settings->ToggleType() && me->type() == QMouseEvent::MouseButtonRelease && me->button() == Qt::RightButton)))
{
if (m_DoubleClickLowVal == value())
{
m_DoubleClickZeroEvent(this, m_DoubleClickZero);
setValue(m_DoubleClickZero);
}
else
{
m_DoubleClickNonZeroEvent(this, m_DoubleClickNonZero);
setValue(m_DoubleClickNonZero);
}
e->accept();
return true;
}
}
else if (isEnabled())
{
if (e->type() == QEvent::Wheel)
{
if (QWheelEvent* we = dynamic_cast<QWheelEvent*>(e))
{
const auto shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
const auto ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (we->angleDelta().ry() > 0)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() + m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() + (ctrl ? m_Step * 10 : m_Step));
}
}
else
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() - m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() - (ctrl ? m_Step * 10 : m_Step));
}
}
e->accept();
return true;
}
}
else if (dynamic_cast<QKeyEvent*>(e))
{
e->accept();
return true;
}
}
return QSpinBox::eventFilter(o, e);
}
/// <summary>
/// Override which is for handling specific key presses while this control is focused.
/// In particular, + = and up arrow increase the value, equivalent to scrolling the mouse wheel up, while also observing shift/ctrl modifiers.
/// Values are decreased in the same way by pressing - _ or down arrow.
/// </summary>
/// <param name="ke">The key event</param>
void SpinBox::keyPressEvent(QKeyEvent* ke)
{
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (ke->key() == Qt::Key_Up)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() + m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() + (ctrl ? m_Step * 10 : m_Step));
}
ke->accept();
}
else if (ke->key() == Qt::Key_Down)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() - m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() - (ctrl ? m_Step * 10 : m_Step));
}
ke->accept();
}
else if (ke->key() == Qt::Key_Space)
{
if (m_DoubleClickLowVal == value())
{
m_DoubleClickZeroEvent(this, m_DoubleClickZero);
setValue(m_DoubleClickZero);
}
else
{
m_DoubleClickNonZeroEvent(this, m_DoubleClickNonZero);
setValue(m_DoubleClickNonZero);
}
}
else
QSpinBox::keyPressEvent(ke);
}
/// <summary>
/// Called when focus enters the spinner.
/// </summary>
/// <param name="e">The event</param>
void SpinBox::focusInEvent(QFocusEvent* e)
{
StopTimer();
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)
{
StopTimer();
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(QEnterEvent* e)
{
StopTimer();
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)
{
StopTimer();
QSpinBox::leaveEvent(e);
}
/// <summary>
/// Start the timer in response to the right mouse button being pressed.
/// </summary>
void SpinBox::StartTimer()
{
s_Timer.stop();
connect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
s_Timer.start(300);
}
/// <summary>
/// Stop the timer in response to the left mouse button being pressed.
/// </summary>
void SpinBox::StopTimer()
{
s_Timer.stop();
disconnect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
}

View File

@ -1,62 +1,62 @@
#pragma once
#include "FractoriumPch.h"
#include "DoubleSpinBox.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* p = nullptr, int height = 16, int step = 1);
virtual ~SpinBox() { }
void SetValueStealth(int d);
void SetValueStealth(size_t d);
void DoubleClick(bool b);
void DoubleClickLowVal(int val);
int DoubleClickLowVal();
void DoubleClickZero(int val);
int DoubleClickZero();
void DoubleClickNonZero(int val);
int DoubleClickNonZero();
void SmallStep(int step);
QLineEdit* lineEdit();
std::function<void(SpinBox*, int)> m_DoubleClickZeroEvent = [&](SpinBox*, int) {};
std::function<void(SpinBox*, int)> m_DoubleClickNonZeroEvent = [&](SpinBox*, int) {};
public slots:
void onSpinBoxValueChanged(int i);
void OnTimeout();
protected:
bool eventFilter(QObject* o, QEvent* e) override;
void keyPressEvent(QKeyEvent* event) override;
void focusInEvent(QFocusEvent* e) override;
void focusOutEvent(QFocusEvent* e) override;
void enterEvent(QEvent* e) override;
void leaveEvent(QEvent* e) override;
private:
void StartTimer();
void StopTimer();
bool m_DoubleClick;
int m_DoubleClickLowVal;
int m_DoubleClickNonZero;
int m_DoubleClickZero;
int m_Step;
int m_SmallStep;
QPoint m_MouseDownPoint;
QPoint m_MouseMovePoint;
shared_ptr<FractoriumSettings> m_Settings;
static QTimer s_Timer;
};
#pragma once
#include "FractoriumPch.h"
#include "DoubleSpinBox.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* p = nullptr, int height = 16, int step = 1);
virtual ~SpinBox() { }
void SetValueStealth(int d);
void SetValueStealth(size_t d);
void DoubleClick(bool b);
void DoubleClickLowVal(int val);
int DoubleClickLowVal();
void DoubleClickZero(int val);
int DoubleClickZero();
void DoubleClickNonZero(int val);
int DoubleClickNonZero();
void SmallStep(int step);
QLineEdit* lineEdit();
std::function<void(SpinBox*, int)> m_DoubleClickZeroEvent = [&](SpinBox*, int) {};
std::function<void(SpinBox*, int)> m_DoubleClickNonZeroEvent = [&](SpinBox*, int) {};
public slots:
void onSpinBoxValueChanged(int i);
void OnTimeout();
protected:
bool eventFilter(QObject* o, QEvent* e) override;
void keyPressEvent(QKeyEvent* event) override;
void focusInEvent(QFocusEvent* e) override;
void focusOutEvent(QFocusEvent* e) override;
void enterEvent(QEnterEvent* e) override;
void leaveEvent(QEvent* e) override;
private:
void StartTimer();
void StopTimer();
bool m_DoubleClick;
int m_DoubleClickLowVal;
int m_DoubleClickNonZero;
int m_DoubleClickZero;
int m_Step;
int m_SmallStep;
QPoint m_MouseDownPoint;
QPoint m_MouseMovePoint;
shared_ptr<FractoriumSettings> m_Settings;
static QTimer s_Timer;
};

View File

@ -1,30 +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* p = nullptr) : QComboBox(p) { }
/// <summary>
/// Set the current index of the combo box without triggering signals.
/// </summary>
/// <param name="index">The current index to set</param>
void SetCurrentIndexStealth(int index)
{
blockSignals(true);
setCurrentIndex(index);
blockSignals(false);
}
};
#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* p = nullptr) : QComboBox(p) { }
/// <summary>
/// Set the current index of the combo box without triggering signals.
/// </summary>
/// <param name="index">The current index to set</param>
void SetCurrentIndexStealth(int index)
{
blockSignals(true);
setCurrentIndex(index);
blockSignals(false);
}
};

View File

@ -1,54 +1,54 @@
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// TableWidget class.
/// </summary>
/// <summary>
/// The purpose of this subclass is to allow for dragging the contents of a table cell.
/// It's used in the palette preview table.
/// </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="p">The parent widget</param>
explicit TableWidget(QWidget* p = nullptr)
: QTableWidget(p)
{
viewport()->installEventFilter(this);
}
signals:
void MouseDragged(const QPointF& local, const QPoint& global);
void MouseReleased();
protected:
/// <summary>
/// Event filter to handle dragging and releasing the mouse.
/// Sadly, QTableWidget makes these hard to get to, so we must handle them here.
/// </summary>
/// <param name="obj">The object sending the event</param>
/// <param name="e">The event</param>
/// <returns>The result of calling the base fucntion.</returns>
bool eventFilter(QObject* obj, QEvent* e) override
{
if (e->type() == QEvent::MouseMove)
{
if (const auto me = dynamic_cast<QMouseEvent*>(e))
emit MouseDragged(me->localPos(), me->globalPos());
}
else if (e->type() == QEvent::MouseButtonRelease)
emit MouseReleased();
return QTableWidget::eventFilter(obj, e);
}
};
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// TableWidget class.
/// </summary>
/// <summary>
/// The purpose of this subclass is to allow for dragging the contents of a table cell.
/// It's used in the palette preview table.
/// </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="p">The parent widget</param>
explicit TableWidget(QWidget* p = nullptr)
: QTableWidget(p)
{
viewport()->installEventFilter(this);
}
signals:
void MouseDragged(const QPointF& local, const QPointF& global);
void MouseReleased();
protected:
/// <summary>
/// Event filter to handle dragging and releasing the mouse.
/// Sadly, QTableWidget makes these hard to get to, so we must handle them here.
/// </summary>
/// <param name="obj">The object sending the event</param>
/// <param name="e">The event</param>
/// <returns>The result of calling the base fucntion.</returns>
bool eventFilter(QObject* obj, QEvent* e) override
{
if (e->type() == QEvent::MouseMove)
{
if (const auto me = dynamic_cast<const QMouseEvent*>(e))
emit MouseDragged(me->position(), me->globalPosition());
}
else if (e->type() == QEvent::MouseButtonRelease)
emit MouseReleased();
return QTableWidget::eventFilter(obj, e);
}
};

View File

@ -1,230 +1,230 @@
#pragma once
#include "FractoriumPch.h"
#include "SpinBox.h"
#include "DoubleSpinBox.h"
/// <summary>
/// TwoButtonComboWidget 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 TwoButtonComboWidget : 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="p">The parent widget</param>
TwoButtonComboWidget(const QString& caption1, const QString& caption2, QStringList comboStrings, int w1, int w2, int h, QWidget* p)
: QWidget(p)
{
QHBoxLayout* l = new QHBoxLayout(this);
m_Button1 = new QPushButton(caption1, p);
m_Button2 = new QPushButton(caption2, p);
m_Combo = new QComboBox(p);
m_Combo->addItems(comboStrings);
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);
m_Combo->setMinimumHeight(h - 3);
m_Combo->setMaximumHeight(h - 3);
l->addWidget(m_Combo);
l->addWidget(m_Button1);
l->addWidget(m_Button2);
l->setAlignment(Qt::AlignLeft);
l->setMargin(0);
l->setSpacing(2);
setLayout(l);
}
QPushButton* m_Button1;
QPushButton* m_Button2;
QComboBox* m_Combo;
};
/// <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="p">The parent widget</param>
SpinnerButtonWidget(DoubleSpinBox* spinBox, QString buttonCaption, int w, int h, QWidget* p)
: QWidget(p)
{
QHBoxLayout* l = new QHBoxLayout(this);
m_Button = new QPushButton(buttonCaption, p);
m_SpinBox = spinBox;
if (w != -1)
{
m_Button->setMinimumWidth(w);
m_Button->setMaximumWidth(w);
}
m_Button->setMinimumHeight(h);
m_Button->setMaximumHeight(h);
l->addWidget(spinBox);
l->addWidget(m_Button);
l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
l->setMargin(0);
l->setSpacing(0);
setLayout(l);
}
DoubleSpinBox* m_SpinBox;
QPushButton* m_Button;
};
/// <summary>
/// Thin container that is both a widget and a container of one DoubleSpinBox and one QLabel.
/// 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 SpinnerLabelWidget : public QWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes the parent to the base, then creates a QLabel,
/// then creates a QPushButton and sets up its caption and dimensions, then
/// assigns the DoubleSpinBox.
/// </summary>
/// <param name="spinBox">The pre-created DoubleSpinBox</param>
/// <param name="h">The height of the label</param>
/// <param name="p">The parent widget</param>
SpinnerLabelWidget(DoubleSpinBox* spinBox, int h, QWidget* p)
: QWidget(p)
{
m_L = new QHBoxLayout(this);
m_SpinBox = spinBox;
m_Label = new QLabel(p);
m_Label->setMinimumHeight(h);
m_Label->setMaximumHeight(h);
m_L->addWidget(spinBox);
m_L->addWidget(m_Label);
m_L->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_L->setMargin(0);
m_L->setSpacing(0);
setLayout(m_L);
}
DoubleSpinBox* m_SpinBox;
QLabel* m_Label;
protected:
QHBoxLayout* m_L;
};
/// <summary>
/// Thin derivation that adds a button to a SpinnerLabelWidget.
/// </summary>
class SpinnerLabelButtonWidget : public SpinnerLabelWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes the parent to the base, then creates a QLabel,
/// then creates a QPushButton and sets up its caption and dimensions, then
/// 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="p">The parent widget</param>
SpinnerLabelButtonWidget(DoubleSpinBox* spinBox, QString buttonCaption, int w, int h, QWidget* p)
: SpinnerLabelWidget(spinBox, h, p)
{
m_Button = new QPushButton(buttonCaption, p);
if (w != -1)
{
m_Button->setMinimumWidth(w);
m_Button->setMaximumWidth(w);
}
m_Button->setMinimumHeight(h);
m_Button->setMaximumHeight(h);
m_L->addWidget(m_Button);
}
QPushButton* m_Button;
};
/// <summary>
/// Thin container that is both a widget and a container of one DoubleSpinBox and one SpinBox.
/// 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 DoubleIntSpinnerWidget : public QWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes the parent to the base, then creates a QHBoxLayout,
/// and adds the passed in DoubleSpinBox and SpinBox to the layout.
/// </summary>
/// <param name="doubleSpinBox">The pre-created DoubleSpinBox</param>
/// <param name="spinBox">The pre-created SpinBox</param>
/// <param name="p">The parent widget</param>
DoubleIntSpinnerWidget(DoubleSpinBox* doubleSpinBox, SpinBox* spinBox, QWidget* p)
: QWidget(p)
{
m_L = new QHBoxLayout(this);
m_DoubleSpinBox = doubleSpinBox;
m_SpinBox = spinBox;
m_L->addWidget(doubleSpinBox);
m_L->addWidget(spinBox);
m_L->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_L->setMargin(0);
m_L->setSpacing(0);
setLayout(m_L);
}
DoubleSpinBox* m_DoubleSpinBox;
SpinBox* m_SpinBox;
protected:
QHBoxLayout* m_L;
};
#pragma once
#include "FractoriumPch.h"
#include "SpinBox.h"
#include "DoubleSpinBox.h"
/// <summary>
/// TwoButtonComboWidget 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 TwoButtonComboWidget : 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="p">The parent widget</param>
TwoButtonComboWidget(const QString& caption1, const QString& caption2, QStringList comboStrings, int w1, int w2, int h, QWidget* p)
: QWidget(p)
{
QHBoxLayout* l = new QHBoxLayout(this);
m_Button1 = new QPushButton(caption1, p);
m_Button2 = new QPushButton(caption2, p);
m_Combo = new QComboBox(p);
m_Combo->addItems(comboStrings);
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);
m_Combo->setMinimumHeight(h - 3);
m_Combo->setMaximumHeight(h - 3);
l->addWidget(m_Combo);
l->addWidget(m_Button1);
l->addWidget(m_Button2);
l->setAlignment(Qt::AlignLeft);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(2);
setLayout(l);
}
QPushButton* m_Button1;
QPushButton* m_Button2;
QComboBox* m_Combo;
};
/// <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="p">The parent widget</param>
SpinnerButtonWidget(DoubleSpinBox* spinBox, QString buttonCaption, int w, int h, QWidget* p)
: QWidget(p)
{
QHBoxLayout* l = new QHBoxLayout(this);
m_Button = new QPushButton(buttonCaption, p);
m_SpinBox = spinBox;
if (w != -1)
{
m_Button->setMinimumWidth(w);
m_Button->setMaximumWidth(w);
}
m_Button->setMinimumHeight(h);
m_Button->setMaximumHeight(h);
l->addWidget(spinBox);
l->addWidget(m_Button);
l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
setLayout(l);
}
DoubleSpinBox* m_SpinBox;
QPushButton* m_Button;
};
/// <summary>
/// Thin container that is both a widget and a container of one DoubleSpinBox and one QLabel.
/// 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 SpinnerLabelWidget : public QWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes the parent to the base, then creates a QLabel,
/// then creates a QPushButton and sets up its caption and dimensions, then
/// assigns the DoubleSpinBox.
/// </summary>
/// <param name="spinBox">The pre-created DoubleSpinBox</param>
/// <param name="h">The height of the label</param>
/// <param name="p">The parent widget</param>
SpinnerLabelWidget(DoubleSpinBox* spinBox, int h, QWidget* p)
: QWidget(p)
{
m_L = new QHBoxLayout(this);
m_SpinBox = spinBox;
m_Label = new QLabel(p);
m_Label->setMinimumHeight(h);
m_Label->setMaximumHeight(h);
m_L->addWidget(spinBox);
m_L->addWidget(m_Label);
m_L->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_L->setContentsMargins(0, 0, 0, 0);
m_L->setSpacing(0);
setLayout(m_L);
}
DoubleSpinBox* m_SpinBox;
QLabel* m_Label;
protected:
QHBoxLayout* m_L;
};
/// <summary>
/// Thin derivation that adds a button to a SpinnerLabelWidget.
/// </summary>
class SpinnerLabelButtonWidget : public SpinnerLabelWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes the parent to the base, then creates a QLabel,
/// then creates a QPushButton and sets up its caption and dimensions, then
/// 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="p">The parent widget</param>
SpinnerLabelButtonWidget(DoubleSpinBox* spinBox, QString buttonCaption, int w, int h, QWidget* p)
: SpinnerLabelWidget(spinBox, h, p)
{
m_Button = new QPushButton(buttonCaption, p);
if (w != -1)
{
m_Button->setMinimumWidth(w);
m_Button->setMaximumWidth(w);
}
m_Button->setMinimumHeight(h);
m_Button->setMaximumHeight(h);
m_L->addWidget(m_Button);
}
QPushButton* m_Button;
};
/// <summary>
/// Thin container that is both a widget and a container of one DoubleSpinBox and one SpinBox.
/// 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 DoubleIntSpinnerWidget : public QWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes the parent to the base, then creates a QHBoxLayout,
/// and adds the passed in DoubleSpinBox and SpinBox to the layout.
/// </summary>
/// <param name="doubleSpinBox">The pre-created DoubleSpinBox</param>
/// <param name="spinBox">The pre-created SpinBox</param>
/// <param name="p">The parent widget</param>
DoubleIntSpinnerWidget(DoubleSpinBox* doubleSpinBox, SpinBox* spinBox, QWidget* p)
: QWidget(p)
{
m_L = new QHBoxLayout(this);
m_DoubleSpinBox = doubleSpinBox;
m_SpinBox = spinBox;
m_L->addWidget(doubleSpinBox);
m_L->addWidget(spinBox);
m_L->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_L->setContentsMargins(0, 0, 0, 0);
m_L->setSpacing(0);
setLayout(m_L);
}
DoubleSpinBox* m_DoubleSpinBox;
SpinBox* m_SpinBox;
protected:
QHBoxLayout* m_L;
};

View File

@ -1,90 +1,90 @@
#pragma once
#include "FractoriumPch.h"
#include "DoubleSpinBox.h"
/// <summary>
/// VariationTreeWidgetItem 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>
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="id">The ID of the variation this widget will represent</param>
/// <param name="p">The parent widget</param>
VariationTreeWidgetItem(eVariationId id, QTreeWidget* p = nullptr)
: QTreeWidgetItem(p)
{
m_Id = id;
}
/// <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="id">The ID of the variation this widget will represent</param>
/// <param name="p">The parent widget</param>
VariationTreeWidgetItem(eVariationId id, QTreeWidgetItem* p = 0)
: QTreeWidgetItem(p)
{
m_Id = id;
}
virtual ~VariationTreeWidgetItem() { }
eVariationId Id() { return m_Id; }
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
{
const auto column = treeWidget()->sortColumn();
auto itemWidget1 = treeWidget()->itemWidget(const_cast<VariationTreeWidgetItem*>(this), 1);//Get the widget for the second column.
if (auto spinBox1 = dynamic_cast<VariationTreeDoubleSpinBox*>(itemWidget1))//Cast the widget to the VariationTreeDoubleSpinBox type.
{
auto itemWidget2 = treeWidget()->itemWidget(const_cast<QTreeWidgetItem*>(&other), 1);//Get the widget for the second column of the widget item passed in.
if (auto spinBox2 = dynamic_cast<VariationTreeDoubleSpinBox*>(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;
const auto weight1 = spinBox1->value();
const auto weight2 = spinBox2->value();
const auto index1 = spinBox1->GetVariationId();
const auto index2 = spinBox2->GetVariationId();
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 std::abs(weight1) < fabs(weight2);
}
}
}
return false;
}
eVariationId m_Id;
};
#pragma once
#include "FractoriumPch.h"
#include "DoubleSpinBox.h"
/// <summary>
/// VariationTreeWidgetItem 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>
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="id">The ID of the variation this widget will represent</param>
/// <param name="p">The parent widget</param>
VariationTreeWidgetItem(eVariationId id, QTreeWidget* p = nullptr)
: QTreeWidgetItem(p)
{
m_Id = id;
}
/// <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="id">The ID of the variation this widget will represent</param>
/// <param name="p">The parent widget</param>
VariationTreeWidgetItem(eVariationId id, QTreeWidgetItem* p = 0)
: QTreeWidgetItem(p)
{
m_Id = id;
}
//virtual ~VariationTreeWidgetItem() { }
eVariationId Id() const { return m_Id; }
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 override
{
const auto column = treeWidget()->sortColumn();
auto itemWidget1 = treeWidget()->itemWidget(const_cast<VariationTreeWidgetItem*>(this), 1);//Get the widget for the second column.
if (auto spinBox1 = dynamic_cast<VariationTreeDoubleSpinBox*>(itemWidget1))//Cast the widget to the VariationTreeDoubleSpinBox type.
{
auto itemWidget2 = treeWidget()->itemWidget(const_cast<QTreeWidgetItem*>(&other), 1);//Get the widget for the second column of the widget item passed in.
if (auto spinBox2 = dynamic_cast<VariationTreeDoubleSpinBox*>(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;
const auto weight1 = spinBox1->value();
const auto weight2 = spinBox2->value();
const auto index1 = spinBox1->GetVariationId();
const auto index2 = spinBox2->GetVariationId();
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 std::abs(weight1) < fabs(weight2);
}
}
}
return false;
}
eVariationId m_Id;
};

View File

@ -1,482 +1,482 @@
#include "FractoriumPch.h"
#include "VariationsDialog.h"
/// <summary>
/// Constructor that takes a parent widget and passes it to the base, then
/// sets up the GUI.
/// </summary>
/// <param name="settings">Pointer to the global settings object to use</param>
/// <param name="p">The parent widget. Default: nullptr.</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumVariationsDialog::FractoriumVariationsDialog(QWidget* p, Qt::WindowFlags f)
: QDialog(p, f),
m_VariationList(VariationList<float>::Instance())
{
ui.setupUi(this);
const auto table = ui.VariationsTable;
m_Settings = FractoriumSettings::DefInstance();
m_Vars = m_Settings->Variations();
Populate();
OnSelectAllButtonClicked(true);
m_CheckBoxes.push_back(ui.SumCheckBox);
m_CheckBoxes.push_back(ui.AssignCheckBox);
m_CheckBoxes.push_back(ui.PpSumCheckBox);
m_CheckBoxes.push_back(ui.PpAssignCheckBox);
m_CheckBoxes.push_back(ui.DcCheckBox);
m_CheckBoxes.push_back(ui.StateCheckBox);
m_CheckBoxes.push_back(ui.ParamCheckBox);
m_CheckBoxes.push_back(ui.NonParamCheckBox);
ui.SumCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterSum ()));
ui.AssignCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterAssign ()));
ui.PpSumCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterPpsum ()));
ui.PpAssignCheckBox->setCheckState(static_cast<Qt::CheckState>(m_Settings->VarFilterPpassign()));
ui.DcCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterSdc ()));
ui.StateCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterState ()));
ui.ParamCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterParam ()));
ui.NonParamCheckBox->setCheckState(static_cast<Qt::CheckState>(m_Settings->VarFilterNonparam()));
for (auto& cb : m_CheckBoxes)
{
if (cb->checkState() == Qt::CheckState::PartiallyChecked)
{
auto font = cb->font();
font.setStrikeOut(true);
cb->setFont(font);
}
}
table->verticalHeader()->setSectionsClickable(true);
table->horizontalHeader()->setSectionsClickable(true);
table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
connect(table, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(OnVariationsTableItemChanged(QTableWidgetItem*)), Qt::QueuedConnection);
connect(ui.SelectAllButton, SIGNAL(clicked(bool)), this, SLOT(OnSelectAllButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.InvertSelectionButton, SIGNAL(clicked(bool)), this, SLOT(OnInvertSelectionButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SelectNoneButton, SIGNAL(clicked(bool)), this, SLOT(OnSelectNoneButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SumCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.AssignCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.PpSumCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.PpAssignCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.DcCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.StateCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.ParamCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.NonParamCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
}
/// <summary>
/// A wrapper to iterate over every table widget item and perform the passed in function on it.
/// </summary>
/// <param name="func">Function to call on each object</param>
void FractoriumVariationsDialog::ForEachCell(std::function<void(QTableWidgetItem* cb)> func)
{
const auto table = ui.VariationsTable;
const auto rows = table->rowCount();
const auto cols = table->columnCount();
table->model()->blockSignals(true);
for (auto row = 0; row < rows; row++)
for (auto col = 0; col < cols; col++)
if (const auto cb = table->item(row, col))
func(cb);
table->model()->blockSignals(false);
table->model()->layoutChanged();
}
/// <summary>
/// A wrapper to iterate over every selected table widget item and perform the passed in function on it.
/// </summary>
/// <param name="func">Function to call on each object</param>
void FractoriumVariationsDialog::ForEachSelectedCell(std::function<void(QTableWidgetItem* cb)> func)
{
const auto table = ui.VariationsTable;
const auto selectedItems = table->selectedItems();
table->model()->blockSignals(true);
for (auto item : selectedItems)
if (item)
func(item);
table->model()->blockSignals(false);
table->model()->layoutChanged();
}
/// <summary>
/// Copy the values of the checkboxes to the map.
/// </summary>
void FractoriumVariationsDialog::SyncSettings()
{
QMap<QString, QVariant> m;
ForEachCell([&](QTableWidgetItem * cb)
{
if (!cb->text().isEmpty())
m[cb->text()] = cb->checkState() == Qt::CheckState::Checked;
});
m_Settings->Variations(m);
m_Settings->VarFilterSum (static_cast<int>(ui.SumCheckBox->checkState()));
m_Settings->VarFilterAssign (static_cast<int>(ui.AssignCheckBox->checkState()));
m_Settings->VarFilterPpsum (static_cast<int>(ui.PpSumCheckBox->checkState()));
m_Settings->VarFilterPpassign(static_cast<int>(ui.PpAssignCheckBox->checkState()));
m_Settings->VarFilterSdc (static_cast<int>(ui.DcCheckBox->checkState()));
m_Settings->VarFilterState (static_cast<int>(ui.StateCheckBox->checkState()));
m_Settings->VarFilterParam (static_cast<int>(ui.ParamCheckBox->checkState()));
m_Settings->VarFilterNonparam(static_cast<int>(ui.NonParamCheckBox->checkState()));
}
/// <summary>
/// Return a const reference to the map.
/// This will contains the state of the checkboxes after
/// the user clicks ok.
/// </summary>
const QMap<QString, QVariant>& FractoriumVariationsDialog::Map()
{
return m_Vars;
}
/// <summary>
/// Clears the type checkboxes without triggering any events.
/// </summary>
void FractoriumVariationsDialog::ClearTypesStealth()
{
for (auto& cb : m_CheckBoxes)
{
cb->blockSignals(true);
cb->setCheckState(Qt::CheckState::Unchecked);
auto f = cb->font();
f.setStrikeOut(cb->checkState() == Qt::CheckState::PartiallyChecked);
cb->setFont(f);
cb->blockSignals(false);
}
}
/// <summary>
/// Check all of the checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void FractoriumVariationsDialog::OnSelectAllButtonClicked(bool checked)
{
ClearTypesStealth();
ForEachCell([&](QTableWidgetItem * cb) { cb->setCheckState(Qt::CheckState::Checked); });
}
/// <summary>
/// Invert the selection state of the checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void FractoriumVariationsDialog::OnInvertSelectionButtonClicked(bool checked)
{
ClearTypesStealth();
ForEachCell([&](QTableWidgetItem * cb)
{
if (cb->checkState() != Qt::CheckState::Checked)
cb->setCheckState(Qt::CheckState::Checked);
else
cb->setCheckState(Qt::CheckState::Unchecked);
});
}
/// <summary>
/// Uncheck all of the checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void FractoriumVariationsDialog::OnSelectNoneButtonClicked(bool checked)
{
ClearTypesStealth();
ForEachCell([&](QTableWidgetItem * cb) { cb->setCheckState(Qt::CheckState::Unchecked); });
}
/// <summary>
/// Called when any of the selection checkboxes change state.
/// This will re-evaluate the entire grid of checkboxes.
/// </summary>
/// <param name="i">Ignored</param>
void FractoriumVariationsDialog::OnSelectionCheckBoxStateChanged(int i)
{
static vector<string> dc{ "m_ColorX" };
static vector<string> assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =",
"outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" };
static std::set<eVariationId> excluded;
excluded.clear();
if (const auto s = dynamic_cast<QCheckBox*>(sender()))
{
auto f = s->font();
f.setStrikeOut(s->checkState() == Qt::CheckState::PartiallyChecked);
s->setFont(f);
}
ForEachCell([&](QTableWidgetItem * cb) { cb->setCheckState(Qt::CheckState::Unchecked); });
ForEachCell([&](QTableWidgetItem * cb)
{
if (const auto var = m_VariationList->GetVariation(cb->text().toStdString()))
{
if (ui.StateCheckBox->checkState() != Qt::CheckState::Unchecked)
{
if (!var->StateOpenCLString().empty())
{
if (ui.StateCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.SumCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (var->VarType() == eVariationType::VARTYPE_REG && !SearchVar(var, assign, false))
{
if (ui.SumCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.AssignCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (var->VarType() == eVariationType::VARTYPE_REG && SearchVar(var, assign, false))
{
if (ui.AssignCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.DcCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (SearchVar(var, dc, false))
{
if (ui.DcCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.NonParamCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (!m_VariationList->GetParametricVariation(cb->text().toStdString()))
{
if (ui.NonParamCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
}
if (ui.PpSumCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (const auto pre = m_VariationList->GetPreVariation(cb->text().toStdString()))
{
if (pre->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
{
if (ui.PpSumCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(pre->VariationId());
}
else if (!Contains(excluded, pre->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (const auto post = m_VariationList->GetPostVariation(cb->text().toStdString()))
{
if (post->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
{
if (ui.PpSumCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(post->VariationId());
}
else if (!Contains(excluded, post->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
}
if (ui.PpAssignCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (const auto pre = m_VariationList->GetPreVariation(cb->text().toStdString()))
{
if (pre->AssignType() == eVariationAssignType::ASSIGNTYPE_SET)
{
if (ui.PpAssignCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(pre->VariationId());
}
else if (!Contains(excluded, pre->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (const auto post = m_VariationList->GetPostVariation(cb->text().toStdString()))
{
if (post->AssignType() == eVariationAssignType::ASSIGNTYPE_SET)
{
if (ui.PpAssignCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(post->VariationId());
}
else if (!Contains(excluded, post->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
}
if (ui.ParamCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (const auto parVar = m_VariationList->GetParametricVariation(cb->text().toStdString()))
{
if (ui.ParamCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(parVar->VariationId());
}
else if (!Contains(excluded, parVar->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
});
}
/// <summary>
/// Create all checkboxes and check them according to the map.
/// </summary>
void FractoriumVariationsDialog::Populate()
{
const auto table = ui.VariationsTable;
const auto size = static_cast<int>(std::max<size_t>(std::max<size_t>(m_VariationList->RegSize(), m_VariationList->PreSize()), m_VariationList->PostSize()));
table->setRowCount(size);
for (auto i = 0; i < size; i++)
{
if (const auto pre = m_VariationList->GetVariation(i, eVariationType::VARTYPE_PRE))
{
const auto cb = new QTableWidgetItem(pre->Name().c_str());
table->setItem(i, 0, cb);
SetCheckFromMap(cb, pre);
}
if (const auto reg = m_VariationList->GetVariation(i, eVariationType::VARTYPE_REG))
{
const auto cb = new QTableWidgetItem(reg->Name().c_str());
table->setItem(i, 1, cb);
SetCheckFromMap(cb, reg);
}
if (const auto post = m_VariationList->GetVariation(i, eVariationType::VARTYPE_POST))
{
const auto cb = new QTableWidgetItem(post->Name().c_str());
table->setItem(i, 2, cb);
SetCheckFromMap(cb, post);
}
}
}
/// <summary>
/// Called when a checkbox changes state.
/// There is a slight hack here to apply the change to all selected checkboxes
/// if ctrl is pressed.
/// Otherwise it will only apply to the checkbox that was clicked.
/// </summary>
/// <param name="item"></param>
void FractoriumVariationsDialog::OnVariationsTableItemChanged(QTableWidgetItem* item)
{
const auto ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (ctrl)
ForEachSelectedCell([&](QTableWidgetItem * cb) { cb->setCheckState(item->checkState()); });
}
/// <summary>
/// Called when the user clicks ok.
/// Copy the state of the checkboxes to the map.
/// </summary>
void FractoriumVariationsDialog::accept()
{
GuiToData();
QDialog::accept();
}
/// <summary>
/// Called when the user clicks cancel.
/// Reset the state of the the checkboxes to what the map previously was
/// when the dialog was shown.
/// </summary>
void FractoriumVariationsDialog::reject()
{
DataToGui();
QDialog::reject();
}
/// <summary>
/// Copy the state of the map to the checkboxes and show the dialog.
/// </summary>
/// <param name="e">Event, passed to base.</param>
void FractoriumVariationsDialog::showEvent(QShowEvent* e)
{
DataToGui();
QDialog::showEvent(e);
}
/// <summary>
/// Copy the values in the map to the state of the checkboxes.
/// </summary>
void FractoriumVariationsDialog::DataToGui()
{
ForEachCell([&](QTableWidgetItem * cb)
{
if (const auto var = m_VariationList->GetVariation(cb->text().toStdString()))
SetCheckFromMap(cb, var);
});
}
/// <summary>
/// Copy the state of the checkboxes to the map.
/// </summary>
void FractoriumVariationsDialog::GuiToData()
{
ForEachCell([&](QTableWidgetItem * cb)
{
if (const auto var = m_VariationList->GetVariation(cb->text().toStdString()))
m_Vars[cb->text()] = (cb->checkState() == Qt::Checked);
});
}
/// <summary>
/// Set the state of the passed in table item checkbox based on the boolean contained
/// in the map for the passed in variation.
/// </summary>
/// <param name="cb">The checkbox to check</param>
/// <param name="var">That variation to be looked up in the map</param>
void FractoriumVariationsDialog::SetCheckFromMap(QTableWidgetItem* cb, const Variation<float>* var)
{
if (!m_Vars.contains(var->Name().c_str()))
{
cb->setCheckState(Qt::Checked);
}
else
{
const auto chk = m_Vars[var->Name().c_str()].toBool();
cb->setCheckState(chk ? Qt::Checked : Qt::Unchecked);
}
}
#include "FractoriumPch.h"
#include "VariationsDialog.h"
/// <summary>
/// Constructor that takes a parent widget and passes it to the base, then
/// sets up the GUI.
/// </summary>
/// <param name="settings">Pointer to the global settings object to use</param>
/// <param name="p">The parent widget. Default: nullptr.</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumVariationsDialog::FractoriumVariationsDialog(QWidget* p, Qt::WindowFlags f)
: QDialog(p, f),
m_VariationList(VariationList<float>::Instance())
{
ui.setupUi(this);
const auto table = ui.VariationsTable;
m_Settings = FractoriumSettings::DefInstance();
m_Vars = m_Settings->Variations();
Populate();
OnSelectAllButtonClicked(true);
m_CheckBoxes.push_back(ui.SumCheckBox);
m_CheckBoxes.push_back(ui.AssignCheckBox);
m_CheckBoxes.push_back(ui.PpSumCheckBox);
m_CheckBoxes.push_back(ui.PpAssignCheckBox);
m_CheckBoxes.push_back(ui.DcCheckBox);
m_CheckBoxes.push_back(ui.StateCheckBox);
m_CheckBoxes.push_back(ui.ParamCheckBox);
m_CheckBoxes.push_back(ui.NonParamCheckBox);
ui.SumCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterSum ()));
ui.AssignCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterAssign ()));
ui.PpSumCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterPpsum ()));
ui.PpAssignCheckBox->setCheckState(static_cast<Qt::CheckState>(m_Settings->VarFilterPpassign()));
ui.DcCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterSdc ()));
ui.StateCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterState ()));
ui.ParamCheckBox->setCheckState (static_cast<Qt::CheckState>(m_Settings->VarFilterParam ()));
ui.NonParamCheckBox->setCheckState(static_cast<Qt::CheckState>(m_Settings->VarFilterNonparam()));
for (auto& cb : m_CheckBoxes)
{
if (cb->checkState() == Qt::CheckState::PartiallyChecked)
{
auto font = cb->font();
font.setStrikeOut(true);
cb->setFont(font);
}
}
table->verticalHeader()->setSectionsClickable(true);
table->horizontalHeader()->setSectionsClickable(true);
table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
connect(table, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(OnVariationsTableItemChanged(QTableWidgetItem*)), Qt::QueuedConnection);
connect(ui.SelectAllButton, SIGNAL(clicked(bool)), this, SLOT(OnSelectAllButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.InvertSelectionButton, SIGNAL(clicked(bool)), this, SLOT(OnInvertSelectionButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SelectNoneButton, SIGNAL(clicked(bool)), this, SLOT(OnSelectNoneButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SumCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.AssignCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.PpSumCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.PpAssignCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.DcCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.StateCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.ParamCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.NonParamCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSelectionCheckBoxStateChanged(int)), Qt::QueuedConnection);
}
/// <summary>
/// A wrapper to iterate over every table widget item and perform the passed in function on it.
/// </summary>
/// <param name="func">Function to call on each object</param>
void FractoriumVariationsDialog::ForEachCell(std::function<void(QTableWidgetItem* cb)> func)
{
const auto table = ui.VariationsTable;
const auto rows = table->rowCount();
const auto cols = table->columnCount();
table->model()->blockSignals(true);
for (auto row = 0; row < rows; row++)
for (auto col = 0; col < cols; col++)
if (const auto cb = table->item(row, col))
func(cb);
table->model()->blockSignals(false);
table->model()->layoutChanged();
}
/// <summary>
/// A wrapper to iterate over every selected table widget item and perform the passed in function on it.
/// </summary>
/// <param name="func">Function to call on each object</param>
void FractoriumVariationsDialog::ForEachSelectedCell(std::function<void(QTableWidgetItem* cb)> func)
{
const auto table = ui.VariationsTable;
const auto selectedItems = table->selectedItems();
table->model()->blockSignals(true);
for (auto item : selectedItems)
if (item)
func(item);
table->model()->blockSignals(false);
table->model()->layoutChanged();
}
/// <summary>
/// Copy the values of the checkboxes to the map.
/// </summary>
void FractoriumVariationsDialog::SyncSettings()
{
QMap<QString, QVariant> m;
ForEachCell([&](QTableWidgetItem * cb)
{
if (!cb->text().isEmpty())
m[cb->text()] = cb->checkState() == Qt::CheckState::Checked;
});
m_Settings->Variations(m);
m_Settings->VarFilterSum (static_cast<int>(ui.SumCheckBox->checkState()));
m_Settings->VarFilterAssign (static_cast<int>(ui.AssignCheckBox->checkState()));
m_Settings->VarFilterPpsum (static_cast<int>(ui.PpSumCheckBox->checkState()));
m_Settings->VarFilterPpassign(static_cast<int>(ui.PpAssignCheckBox->checkState()));
m_Settings->VarFilterSdc (static_cast<int>(ui.DcCheckBox->checkState()));
m_Settings->VarFilterState (static_cast<int>(ui.StateCheckBox->checkState()));
m_Settings->VarFilterParam (static_cast<int>(ui.ParamCheckBox->checkState()));
m_Settings->VarFilterNonparam(static_cast<int>(ui.NonParamCheckBox->checkState()));
}
/// <summary>
/// Return a const reference to the map.
/// This will contains the state of the checkboxes after
/// the user clicks ok.
/// </summary>
const QMap<QString, QVariant>& FractoriumVariationsDialog::Map()
{
return m_Vars;
}
/// <summary>
/// Clears the type checkboxes without triggering any events.
/// </summary>
void FractoriumVariationsDialog::ClearTypesStealth()
{
for (auto& cb : m_CheckBoxes)
{
cb->blockSignals(true);
cb->setCheckState(Qt::CheckState::Unchecked);
auto f = cb->font();
f.setStrikeOut(cb->checkState() == Qt::CheckState::PartiallyChecked);
cb->setFont(f);
cb->blockSignals(false);
}
}
/// <summary>
/// Check all of the checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void FractoriumVariationsDialog::OnSelectAllButtonClicked(bool checked)
{
ClearTypesStealth();
ForEachCell([&](QTableWidgetItem * cb) { cb->setCheckState(Qt::CheckState::Checked); });
}
/// <summary>
/// Invert the selection state of the checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void FractoriumVariationsDialog::OnInvertSelectionButtonClicked(bool checked)
{
ClearTypesStealth();
ForEachCell([&](QTableWidgetItem * cb)
{
if (cb->checkState() != Qt::CheckState::Checked)
cb->setCheckState(Qt::CheckState::Checked);
else
cb->setCheckState(Qt::CheckState::Unchecked);
});
}
/// <summary>
/// Uncheck all of the checkboxes.
/// </summary>
/// <param name="checked">Ignored</param>
void FractoriumVariationsDialog::OnSelectNoneButtonClicked(bool checked)
{
ClearTypesStealth();
ForEachCell([&](QTableWidgetItem * cb) { cb->setCheckState(Qt::CheckState::Unchecked); });
}
/// <summary>
/// Called when any of the selection checkboxes change state.
/// This will re-evaluate the entire grid of checkboxes.
/// </summary>
/// <param name="i">Ignored</param>
void FractoriumVariationsDialog::OnSelectionCheckBoxStateChanged(int i)
{
static vector<string> dc{ "m_ColorX" };
static vector<string> assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =",
"outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" };
static std::set<eVariationId> excluded;
excluded.clear();
if (const auto s = dynamic_cast<QCheckBox*>(sender()))
{
auto f = s->font();
f.setStrikeOut(s->checkState() == Qt::CheckState::PartiallyChecked);
s->setFont(f);
}
ForEachCell([&](QTableWidgetItem * cb) { cb->setCheckState(Qt::CheckState::Unchecked); });
ForEachCell([&](QTableWidgetItem * cb)
{
if (const auto var = m_VariationList->GetVariation(cb->text().toStdString()))
{
if (ui.StateCheckBox->checkState() != Qt::CheckState::Unchecked)
{
if (!var->StateOpenCLString().empty())
{
if (ui.StateCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.SumCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (var->VarType() == eVariationType::VARTYPE_REG && !SearchVar(var, assign, false))
{
if (ui.SumCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.AssignCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (var->VarType() == eVariationType::VARTYPE_REG && SearchVar(var, assign, false))
{
if (ui.AssignCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.DcCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (SearchVar(var, dc, false))
{
if (ui.DcCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (ui.NonParamCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (!m_VariationList->GetParametricVariation(cb->text().toStdString()))
{
if (ui.NonParamCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(var->VariationId());
}
else if (!Contains(excluded, var->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
}
if (ui.PpSumCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (const auto pre = m_VariationList->GetPreVariation(cb->text().toStdString()))
{
if (pre->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
{
if (ui.PpSumCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(pre->VariationId());
}
else if (!Contains(excluded, pre->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (const auto post = m_VariationList->GetPostVariation(cb->text().toStdString()))
{
if (post->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
{
if (ui.PpSumCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(post->VariationId());
}
else if (!Contains(excluded, post->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
}
if (ui.PpAssignCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (const auto pre = m_VariationList->GetPreVariation(cb->text().toStdString()))
{
if (pre->AssignType() == eVariationAssignType::ASSIGNTYPE_SET)
{
if (ui.PpAssignCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(pre->VariationId());
}
else if (!Contains(excluded, pre->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
if (const auto post = m_VariationList->GetPostVariation(cb->text().toStdString()))
{
if (post->AssignType() == eVariationAssignType::ASSIGNTYPE_SET)
{
if (ui.PpAssignCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(post->VariationId());
}
else if (!Contains(excluded, post->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
}
if (ui.ParamCheckBox->isChecked() != Qt::CheckState::Unchecked)
{
if (const auto parVar = m_VariationList->GetParametricVariation(cb->text().toStdString()))
{
if (ui.ParamCheckBox->checkState() == Qt::CheckState::PartiallyChecked)
{
cb->setCheckState(Qt::CheckState::Unchecked);
excluded.insert(parVar->VariationId());
}
else if (!Contains(excluded, parVar->VariationId()))
cb->setCheckState(Qt::CheckState::Checked);
}
}
});
}
/// <summary>
/// Create all checkboxes and check them according to the map.
/// </summary>
void FractoriumVariationsDialog::Populate()
{
const auto table = ui.VariationsTable;
const auto size = static_cast<int>(std::max<size_t>(std::max<size_t>(m_VariationList->RegSize(), m_VariationList->PreSize()), m_VariationList->PostSize()));
table->setRowCount(size);
for (auto i = 0; i < size; i++)
{
if (const auto pre = m_VariationList->GetVariation(i, eVariationType::VARTYPE_PRE))
{
const auto cb = new QTableWidgetItem(pre->Name().c_str());
table->setItem(i, 0, cb);
SetCheckFromMap(cb, pre);
}
if (const auto reg = m_VariationList->GetVariation(i, eVariationType::VARTYPE_REG))
{
const auto cb = new QTableWidgetItem(reg->Name().c_str());
table->setItem(i, 1, cb);
SetCheckFromMap(cb, reg);
}
if (const auto post = m_VariationList->GetVariation(i, eVariationType::VARTYPE_POST))
{
const auto cb = new QTableWidgetItem(post->Name().c_str());
table->setItem(i, 2, cb);
SetCheckFromMap(cb, post);
}
}
}
/// <summary>
/// Called when a checkbox changes state.
/// There is a slight hack here to apply the change to all selected checkboxes
/// if ctrl is pressed.
/// Otherwise it will only apply to the checkbox that was clicked.
/// </summary>
/// <param name="item"></param>
void FractoriumVariationsDialog::OnVariationsTableItemChanged(QTableWidgetItem* item)
{
const auto ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (ctrl)
ForEachSelectedCell([&](QTableWidgetItem * cb) { cb->setCheckState(item->checkState()); });
}
/// <summary>
/// Called when the user clicks ok.
/// Copy the state of the checkboxes to the map.
/// </summary>
void FractoriumVariationsDialog::accept()
{
GuiToData();
QDialog::accept();
}
/// <summary>
/// Called when the user clicks cancel.
/// Reset the state of the the checkboxes to what the map previously was
/// when the dialog was shown.
/// </summary>
void FractoriumVariationsDialog::reject()
{
DataToGui();
QDialog::reject();
}
/// <summary>
/// Copy the state of the map to the checkboxes and show the dialog.
/// </summary>
/// <param name="e">Event, passed to base.</param>
void FractoriumVariationsDialog::showEvent(QShowEvent* e)
{
DataToGui();
QDialog::showEvent(e);
}
/// <summary>
/// Copy the values in the map to the state of the checkboxes.
/// </summary>
void FractoriumVariationsDialog::DataToGui()
{
ForEachCell([&](QTableWidgetItem * cb)
{
if (const auto var = m_VariationList->GetVariation(cb->text().toStdString()))
SetCheckFromMap(cb, var);
});
}
/// <summary>
/// Copy the state of the checkboxes to the map.
/// </summary>
void FractoriumVariationsDialog::GuiToData()
{
ForEachCell([&](QTableWidgetItem * cb)
{
if (const auto var = m_VariationList->GetVariation(cb->text().toStdString()))
m_Vars[cb->text()] = (cb->checkState() == Qt::Checked);
});
}
/// <summary>
/// Set the state of the passed in table item checkbox based on the boolean contained
/// in the map for the passed in variation.
/// </summary>
/// <param name="cb">The checkbox to check</param>
/// <param name="var">That variation to be looked up in the map</param>
void FractoriumVariationsDialog::SetCheckFromMap(QTableWidgetItem* cb, const Variation<float>* var)
{
if (!m_Vars.contains(var->Name().c_str()))
{
cb->setCheckState(Qt::Checked);
}
else
{
const auto chk = m_Vars[var->Name().c_str()].toBool();
cb->setCheckState(chk ? Qt::Checked : Qt::Unchecked);
}
}

View File

@ -1,51 +1,51 @@
#pragma once
#include "ui_VariationsDialog.h"
#include "FractoriumSettings.h"
/// <summary>
/// FractoriumVariationsDialog class.
/// </summary>
/// <summary>
/// The variations filter dialog displays several columns
/// with the different types of variations shown as checkboxes.
/// This is used to filter the variations that are shown in the main window
/// because the list is very long.
/// The results are stored in a map and returned.
/// These are used in conjunction with the filter edit box to filter what's shown.
/// </summary>
class FractoriumVariationsDialog : public QDialog
{
Q_OBJECT
public:
FractoriumVariationsDialog(QWidget* p = nullptr, Qt::WindowFlags f = nullptr);
void ForEachCell(std::function<void(QTableWidgetItem* cb)> func);
void ForEachSelectedCell(std::function<void(QTableWidgetItem* cb)> func);
void SyncSettings();
const QMap<QString, QVariant>& Map();
public slots:
void OnSelectAllButtonClicked(bool checked);
void OnInvertSelectionButtonClicked(bool checked);
void OnSelectNoneButtonClicked(bool checked);
void OnSelectionCheckBoxStateChanged(int i);
void OnVariationsTableItemChanged(QTableWidgetItem* item);
void accept() override;
void reject() override;
protected:
void showEvent(QShowEvent* e) override;
private:
void ClearTypesStealth();
void DataToGui();
void GuiToData();
void Populate();
void SetCheckFromMap(QTableWidgetItem* cb, const Variation<float>* var);
shared_ptr<VariationList<float>> m_VariationList;
vector<QCheckBox*> m_CheckBoxes;
QMap<QString, QVariant> m_Vars;
shared_ptr<FractoriumSettings> m_Settings;
Ui::VariationsDialog ui;
};
#pragma once
#include "ui_VariationsDialog.h"
#include "FractoriumSettings.h"
/// <summary>
/// FractoriumVariationsDialog class.
/// </summary>
/// <summary>
/// The variations filter dialog displays several columns
/// with the different types of variations shown as checkboxes.
/// This is used to filter the variations that are shown in the main window
/// because the list is very long.
/// The results are stored in a map and returned.
/// These are used in conjunction with the filter edit box to filter what's shown.
/// </summary>
class FractoriumVariationsDialog : public QDialog
{
Q_OBJECT
public:
FractoriumVariationsDialog(QWidget* p = nullptr, Qt::WindowFlags f = Qt::WindowType::Widget);
void ForEachCell(std::function<void(QTableWidgetItem* cb)> func);
void ForEachSelectedCell(std::function<void(QTableWidgetItem* cb)> func);
void SyncSettings();
const QMap<QString, QVariant>& Map();
public slots:
void OnSelectAllButtonClicked(bool checked);
void OnInvertSelectionButtonClicked(bool checked);
void OnSelectNoneButtonClicked(bool checked);
void OnSelectionCheckBoxStateChanged(int i);
void OnVariationsTableItemChanged(QTableWidgetItem* item);
void accept() override;
void reject() override;
protected:
void showEvent(QShowEvent* e) override;
private:
void ClearTypesStealth();
void DataToGui();
void GuiToData();
void Populate();
void SetCheckFromMap(QTableWidgetItem* cb, const Variation<float>* var);
shared_ptr<VariationList<float>> m_VariationList;
vector<QCheckBox*> m_CheckBoxes;
QMap<QString, QVariant> m_Vars;
shared_ptr<FractoriumSettings> m_Settings;
Ui::VariationsDialog ui;
};

View File

@ -1,295 +1,295 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VariationsDialog</class>
<widget class="QDialog" name="VariationsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>585</width>
<height>517</height>
</rect>
</property>
<property name="windowTitle">
<string>Variations Filter</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="spacing">
<number>4</number>
</property>
<item row="4" column="0">
<widget class="QDialogButtonBox" name="ButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QTableWidget" name="VariationsTable">
<property name="mouseTracking">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="columnCount">
<number>3</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>190</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>19</number>
</attribute>
<column>
<property name="text">
<string>Pre</string>
</property>
</column>
<column>
<property name="text">
<string>Regular</string>
</property>
</column>
<column>
<property name="text">
<string>Post</string>
</property>
</column>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="VariationsHorizontalLayout">
<item>
<widget class="QPushButton" name="SelectAllButton">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="InvertSelectionButton">
<property name="text">
<string>Invert Selection</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SelectNoneButton">
<property name="text">
<string>Select None</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" rowspan="2">
<widget class="QGroupBox" name="TypeGroupBox">
<property name="title">
<string>Select by variation type</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="2">
<widget class="QCheckBox" name="PpSumCheckBox">
<property name="toolTip">
<string>Select all pre/post variations which have the non-standard behavior of summing their outputs</string>
</property>
<property name="text">
<string>PP Sum</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="SumCheckBox">
<property name="toolTip">
<string>Select all regular variations which have the standard behavior of summing their outputs</string>
</property>
<property name="text">
<string>Sum</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="AssignCheckBox">
<property name="toolTip">
<string>Select all regular variations which have the non-standard behavior of assigning their outputs</string>
</property>
<property name="text">
<string>Assign</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="DcCheckBox">
<property name="toolTip">
<string>Select all variations which use direct coloring</string>
</property>
<property name="text">
<string>DC</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="StateCheckBox">
<property name="toolTip">
<string>Select all variations which alter their state on each iteration</string>
</property>
<property name="text">
<string>State</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="ParamCheckBox">
<property name="toolTip">
<string>Select all variations which have parameters</string>
</property>
<property name="text">
<string>Param</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="NonParamCheckBox">
<property name="toolTip">
<string>Select all variations which do not have parameters (weight only)</string>
</property>
<property name="text">
<string>Non Param</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QCheckBox" name="PpAssignCheckBox">
<property name="toolTip">
<string>Select all pre/post variations which have the standard behavior of assigning their outputs</string>
</property>
<property name="text">
<string>PP Assign</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>SelectAllButton</tabstop>
<tabstop>InvertSelectionButton</tabstop>
<tabstop>SelectNoneButton</tabstop>
<tabstop>SumCheckBox</tabstop>
<tabstop>AssignCheckBox</tabstop>
<tabstop>PpSumCheckBox</tabstop>
<tabstop>PpAssignCheckBox</tabstop>
<tabstop>DcCheckBox</tabstop>
<tabstop>StateCheckBox</tabstop>
<tabstop>ParamCheckBox</tabstop>
<tabstop>NonParamCheckBox</tabstop>
<tabstop>VariationsTable</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>ButtonBox</sender>
<signal>accepted()</signal>
<receiver>VariationsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>ButtonBox</sender>
<signal>rejected()</signal>
<receiver>VariationsDialog</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>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VariationsDialog</class>
<widget class="QDialog" name="VariationsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>585</width>
<height>517</height>
</rect>
</property>
<property name="windowTitle">
<string>Variations Filter</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="spacing">
<number>4</number>
</property>
<item row="4" column="0">
<widget class="QDialogButtonBox" name="ButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QTableWidget" name="VariationsTable">
<property name="mouseTracking">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="columnCount">
<number>3</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>190</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>19</number>
</attribute>
<column>
<property name="text">
<string>Pre</string>
</property>
</column>
<column>
<property name="text">
<string>Regular</string>
</property>
</column>
<column>
<property name="text">
<string>Post</string>
</property>
</column>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="VariationsHorizontalLayout">
<item>
<widget class="QPushButton" name="SelectAllButton">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="InvertSelectionButton">
<property name="text">
<string>Invert Selection</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SelectNoneButton">
<property name="text">
<string>Select None</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" rowspan="2">
<widget class="QGroupBox" name="TypeGroupBox">
<property name="title">
<string>Select by variation type</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="2">
<widget class="QCheckBox" name="PpSumCheckBox">
<property name="toolTip">
<string>Select all pre/post variations which have the non-standard behavior of summing their outputs</string>
</property>
<property name="text">
<string>PP Sum</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="SumCheckBox">
<property name="toolTip">
<string>Select all regular variations which have the standard behavior of summing their outputs</string>
</property>
<property name="text">
<string>Sum</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="AssignCheckBox">
<property name="toolTip">
<string>Select all regular variations which have the non-standard behavior of assigning their outputs</string>
</property>
<property name="text">
<string>Assign</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="DcCheckBox">
<property name="toolTip">
<string>Select all variations which use direct coloring</string>
</property>
<property name="text">
<string>DC</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="StateCheckBox">
<property name="toolTip">
<string>Select all variations which alter their state on each iteration</string>
</property>
<property name="text">
<string>State</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="ParamCheckBox">
<property name="toolTip">
<string>Select all variations which have parameters</string>
</property>
<property name="text">
<string>Param</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="NonParamCheckBox">
<property name="toolTip">
<string>Select all variations which do not have parameters (weight only)</string>
</property>
<property name="text">
<string>Non Param</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QCheckBox" name="PpAssignCheckBox">
<property name="toolTip">
<string>Select all pre/post variations which have the standard behavior of assigning their outputs</string>
</property>
<property name="text">
<string>PP Assign</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>SelectAllButton</tabstop>
<tabstop>InvertSelectionButton</tabstop>
<tabstop>SelectNoneButton</tabstop>
<tabstop>SumCheckBox</tabstop>
<tabstop>AssignCheckBox</tabstop>
<tabstop>PpSumCheckBox</tabstop>
<tabstop>PpAssignCheckBox</tabstop>
<tabstop>DcCheckBox</tabstop>
<tabstop>StateCheckBox</tabstop>
<tabstop>ParamCheckBox</tabstop>
<tabstop>NonParamCheckBox</tabstop>
<tabstop>VariationsTable</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>ButtonBox</sender>
<signal>accepted()</signal>
<receiver>VariationsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>ButtonBox</sender>
<signal>rejected()</signal>
<receiver>VariationsDialog</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>

View File

@ -1,178 +1,222 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "FractoriumPch.h"
#include "csshighlighter.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
CssHighlighter::CssHighlighter(QTextDocument *document)
: QSyntaxHighlighter(document)
{
}
void CssHighlighter::highlightBlock(const QString& text)
{
enum Token { ALNUM, LBRACE, RBRACE, COLON, SEMICOLON, COMMA, QUOTE, SLASH, STAR };
static const int transitions[10][9] = {
{ Selector, Property, Selector, Pseudo, Property, Selector, Quote, MaybeComment, Selector }, // Selector
{ Property, Property, Selector, Value, Property, Property, Quote, MaybeComment, Property }, // Property
{ Value, Property, Selector, Value, Property, Value, Quote, MaybeComment, Value }, // Value
{ Pseudo1, Property, Selector, Pseudo2, Selector, Selector, Quote, MaybeComment, Pseudo }, // Pseudo
{ Pseudo1, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo1 }, // Pseudo1
{ Pseudo2, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo2 }, // Pseudo2
{ Quote, Quote, Quote, Quote, Quote, Quote, -1, Quote, Quote }, // Quote
{ -1, -1, -1, -1, -1, -1, -1, -1, Comment }, // MaybeComment
{ Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, MaybeCommentEnd }, // Comment
{ Comment, Comment, Comment, Comment, Comment, Comment, Comment, -1, MaybeCommentEnd } // MaybeCommentEnd
};
int lastIndex = 0;
bool lastWasSlash = false;
int state = previousBlockState(), save_state;
if (state == -1) {
// As long as the text is empty, leave the state undetermined
if (text.isEmpty()) {
setCurrentBlockState(-1);
return;
}
// The initial state is based on the precense of a : and the absense of a {.
// This is because Qt style sheets support both a full stylesheet as well as
// an inline form with just properties.
state = save_state = (text.indexOf(QLatin1Char(':')) > -1 &&
text.indexOf(QLatin1Char('{')) == -1) ? Property : Selector;
} else {
save_state = state>>16;
state &= 0x00ff;
}
if (state == MaybeCommentEnd) {
state = Comment;
} else if (state == MaybeComment) {
state = save_state;
}
for (int i = 0; i < text.length(); i++) {
int token = ALNUM;
const QChar c = text.at(i);
const char a = c.toLatin1();
if (state == Quote) {
if (a == '\\') {
lastWasSlash = true;
} else {
if (a == '\"' && !lastWasSlash) {
token = QUOTE;
}
lastWasSlash = false;
}
} else {
switch (a) {
case '{': token = LBRACE; break;
case '}': token = RBRACE; break;
case ':': token = COLON; break;
case ';': token = SEMICOLON; break;
case ',': token = COMMA; break;
case '\"': token = QUOTE; break;
case '/': token = SLASH; break;
case '*': token = STAR; break;
default: break;
}
}
int new_state = transitions[state][token];
if (new_state != state) {
bool include_token = new_state == MaybeCommentEnd || (state == MaybeCommentEnd && new_state!= Comment)
|| state == Quote;
highlight(text, lastIndex, i-lastIndex+include_token, state);
if (new_state == Comment) {
lastIndex = i-1; // include the slash and star
} else {
lastIndex = i + ((token == ALNUM || new_state == Quote) ? 0 : 1);
}
}
if (new_state == -1) {
state = save_state;
} else if (state <= Pseudo2) {
save_state = state;
state = new_state;
} else {
state = new_state;
}
}
highlight(text, lastIndex, text.length() - lastIndex, state);
setCurrentBlockState(state + (save_state<<16));
}
void CssHighlighter::highlight(const QString &text, int start, int length, int state)
{
if (start >= text.length() || length <= 0)
return;
QTextCharFormat format;
switch (state) {
case Selector:
setFormat(start, length, QColor::fromRgb(43, 145, 175));//Teal, like the Visual Studio default for classes in C++ and C#.
break;
case Property:
setFormat(start, length, Qt::darkBlue);
break;
case Value:
setFormat(start, length, Qt::black);
break;
case Pseudo1:
setFormat(start, length, Qt::darkRed);
break;
case Pseudo2:
setFormat(start, length, Qt::black);
break;
case Quote:
setFormat(start, length, Qt::darkMagenta);
break;
case Comment:
case MaybeCommentEnd:
format.setForeground(Qt::darkGreen);
setFormat(start, length, format);
break;
default:
break;
}
}
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "FractoriumPch.h"
#include "csshighlighter.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
CssHighlighter::CssHighlighter(QTextDocument* document)
: QSyntaxHighlighter(document)
{
}
void CssHighlighter::highlightBlock(const QString& text)
{
enum Token { ALNUM, LBRACE, RBRACE, COLON, SEMICOLON, COMMA, QUOTE, SLASH, STAR };
static const int transitions[10][9] =
{
{ Selector, Property, Selector, Pseudo, Property, Selector, Quote, MaybeComment, Selector }, // Selector
{ Property, Property, Selector, Value, Property, Property, Quote, MaybeComment, Property }, // Property
{ Value, Property, Selector, Value, Property, Value, Quote, MaybeComment, Value }, // Value
{ Pseudo1, Property, Selector, Pseudo2, Selector, Selector, Quote, MaybeComment, Pseudo }, // Pseudo
{ Pseudo1, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo1 }, // Pseudo1
{ Pseudo2, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo2 }, // Pseudo2
{ Quote, Quote, Quote, Quote, Quote, Quote, -1, Quote, Quote }, // Quote
{ -1, -1, -1, -1, -1, -1, -1, -1, Comment }, // MaybeComment
{ Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, MaybeCommentEnd }, // Comment
{ Comment, Comment, Comment, Comment, Comment, Comment, Comment, -1, MaybeCommentEnd } // MaybeCommentEnd
};
int lastIndex = 0;
bool lastWasSlash = false;
int state = previousBlockState(), save_state = 0;
if (state == -1)
{
// As long as the text is empty, leave the state undetermined
if (text.isEmpty())
{
setCurrentBlockState(-1);
return;
}
// The initial state is based on the precense of a : and the absense of a {.
// This is because Qt style sheets support both a full stylesheet as well as
// an inline form with just properties.
state = save_state = (text.indexOf(QLatin1Char(':')) > -1 &&
text.indexOf(QLatin1Char('{')) == -1) ? Property : Selector;
}
else
{
save_state = state >> 16;
state &= 0x00ff;
}
if (state == MaybeCommentEnd)
{
state = Comment;
}
else if (state == MaybeComment)
{
state = save_state;
}
for (int i = 0; i < text.length(); i++)
{
int token = ALNUM;
const QChar c = text.at(i);
const char a = c.toLatin1();
if (state == Quote)
{
if (a == '\\')
{
lastWasSlash = true;
}
else
{
if (a == '\"' && !lastWasSlash)
{
token = QUOTE;
}
lastWasSlash = false;
}
}
else
{
switch (a)
{
case '{': token = LBRACE; break;
case '}': token = RBRACE; break;
case ':': token = COLON; break;
case ';': token = SEMICOLON; break;
case ',': token = COMMA; break;
case '\"': token = QUOTE; break;
case '/': token = SLASH; break;
case '*': token = STAR; break;
default: break;
}
}
const auto new_state = transitions[state][token];
if (new_state != state)
{
const auto include_token = new_state == MaybeCommentEnd || (state == MaybeCommentEnd && new_state != Comment)
|| state == Quote;
highlight(text, lastIndex, i - lastIndex + (include_token ? 1 : 0), state);
if (new_state == Comment)
{
lastIndex = i - 1; // include the slash and star
}
else
{
lastIndex = i + ((token == ALNUM || new_state == Quote) ? 0 : 1);
}
}
if (new_state == -1)
{
state = save_state;
}
else if (state <= Pseudo2)
{
save_state = state;
state = new_state;
}
else
{
state = new_state;
}
}
highlight(text, lastIndex, text.length() - lastIndex, state);
setCurrentBlockState(state + (save_state << 16));
}
void CssHighlighter::highlight(const QString& text, int start, int length, int state)
{
if (start >= text.length() || length <= 0)
return;
QTextCharFormat format;
switch (state)
{
case Selector:
setFormat(start, length, QColor::fromRgb(43, 145, 175));//Teal, like the Visual Studio default for classes in C++ and C#.
break;
case Property:
setFormat(start, length, Qt::darkBlue);
break;
case Value:
setFormat(start, length, Qt::black);
break;
case Pseudo1:
setFormat(start, length, Qt::darkRed);
break;
case Pseudo2:
setFormat(start, length, Qt::black);
break;
case Quote:
setFormat(start, length, Qt::darkMagenta);
break;
case Comment:
case MaybeCommentEnd:
format.setForeground(Qt::darkGreen);
setFormat(start, length, format);
break;
default:
break;
}
}

View File

@ -1,67 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of Qt Designer. This header
// file may change from version to version without notice, or even be removed.
//
// We mean it.
//
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
class CssHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
explicit CssHighlighter(QTextDocument *document);
protected:
void highlightBlock(const QString&);
void highlight(const QString&, int, int, int/*State*/);
private:
enum State { Selector, Property, Value, Pseudo, Pseudo1, Pseudo2, Quote,
MaybeComment, Comment, MaybeCommentEnd };
};
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of Qt Designer. This header
// file may change from version to version without notice, or even be removed.
//
// We mean it.
//
#pragma once
#include "FractoriumPch.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
class CssHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
explicit CssHighlighter(QTextDocument* document);
protected:
void highlightBlock(const QString&) override;
void highlight(const QString&, int, int, int/*State*/);
private:
enum State { Selector, Property, Value, Pseudo, Pseudo1, Pseudo2, Quote,
MaybeComment, Comment, MaybeCommentEnd
};
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,62 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include "FractoriumPch.h"
#include "qcssparser.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
namespace QCss
{
// auto generated. DO NOT EDIT.
class QCssScanner
{
public:
QCssScanner(const QString &inp);
inline QChar next() {
return (pos < input.length()) ? input.at(pos++) : QChar();
}
int handleCommentStart();
int lex();
QString input;
int pos;
int lexemStart;
int lexemLength;
};
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include "FractoriumPch.h"
#include "qcssparser.h"
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
namespace QCss
{
// auto generated. DO NOT EDIT.
class QCssScanner
{
public:
QCssScanner(const QString &inp);
inline QChar next() {
return (pos < input.length()) ? input.at(pos++) : QChar();
}
int handleCommentStart();
int lex();
QString input;
int pos;
int lexemStart;
int lexemLength;
};
}

View File

@ -1,77 +1,77 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of qfunctions_*. This header file may change from version to version
// without notice, or even be removed.
//
// We mean it.
//
#pragma once
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
#if defined(Q_OS_WINCE)
# include "QtCore/qfunctions_wince.h"
#elif defined(Q_OS_VXWORKS)
# include "QtCore/qfunctions_vxworks.h"
#elif defined(Q_OS_NACL)
# include "QtCore/qfunctions_nacl.h"
#elif defined(Q_OS_WINRT)
# include "QtCore/qfunctions_winrt.h"
#endif
#ifdef Q_CC_RVCT
// rvct doesn't see static operators when using our qalgorithms
# define Q_STATIC_GLOBAL_OPERATOR inline
# define Q_STATIC_GLOBAL_INLINE_OPERATOR inline
#else
# define Q_STATIC_GLOBAL_OPERATOR static
# define Q_STATIC_GLOBAL_INLINE_OPERATOR static inline
#endif
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of qfunctions_*. This header file may change from version to version
// without notice, or even be removed.
//
// We mean it.
//
#pragma once
/// <summary>
/// The code in this file did not originate in Fractorium.
/// It was taken either in whole or in part from the source code
/// of Qt Creator. Their license applies.
/// </summary>
#if defined(Q_OS_WINCE)
# include "QtCore/qfunctions_wince.h"
#elif defined(Q_OS_VXWORKS)
# include "QtCore/qfunctions_vxworks.h"
#elif defined(Q_OS_NACL)
# include "QtCore/qfunctions_nacl.h"
#elif defined(Q_OS_WINRT)
# include "QtCore/qfunctions_winrt.h"
#endif
#ifdef Q_CC_RVCT
// rvct doesn't see static operators when using our qalgorithms
# define Q_STATIC_GLOBAL_OPERATOR inline
# define Q_STATIC_GLOBAL_INLINE_OPERATOR inline
#else
# define Q_STATIC_GLOBAL_OPERATOR static
# define Q_STATIC_GLOBAL_INLINE_OPERATOR static inline
#endif

View File

@ -1,17 +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
//{{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