--User changes

-Add animation sequence creation to Fractorium.
 -Add two new options to EmberGenome which are used when generating an animation sequence.:
  --startcount: Add this number to the filename of each flame.
  --padding: Override the automatically calculated amount of padding zeroes added to each filename.

--Bug fixes
 -Prevent filenames in command line programs from using scientific notation when rendering a large number of frames.
 -Fix tab orders to match newer GUI items which were overlooked in previous releases.
 -Re-render previews if transparency value in the options dialog was changed. Re-rendering was previously only done if early clip or y axis up was changed.
 -Use transparency when rendering thumbnail previews.

--Code changes
 -Wrap EmberCommon.h in a namespace called EmberCommon.
 -Move FormatName() from EmberGenome to EmberCommon.h/cpp
 -Add a prefix parameter to EmberFile::DefaultFilename() to allow for creating a default filename for sequences.
 -When showing the final render dialog, allow specifying where it came from: the toolbar or the render sequence button.
 -Refactor all preview rendering code out into its own class hierarchy with overrides for the main window and the final render dialog.
 -Remove all preview render cancelling functions, they are now built into the new class hierarchy and a new render will not start until the previous one is stopped.
 -Add two new function ConstrainLow() and ConstrainHigh() which wrap constraining two min/max spinboxes to each others' values.
 -Add a bool to FractoriumEmberControllerBase::CopyEmberFile() to specify whether to copy the main file or the sequence file. This is somewhat of a hack and was done in a rush.
 -Add a bool to FractoriumEmberControllerBase::SetEmberFile() to specify whether to move the file rather than copy. This is used in FinalRenderEmberController and improves efficiency.
 -Add wrapper functions for variations filter dialog settings.
This commit is contained in:
mfeemster 2016-06-11 17:47:03 -07:00
parent 51c1cc7a83
commit c91171d392
27 changed files with 1734 additions and 450 deletions

View File

@ -1323,6 +1323,16 @@ public:
m_Comment = comment;
}
/// <summary>
/// Set stagger value.
/// Greater than 0 means interpolate xforms one at a time, else interpolate all at once.
/// </summary>
/// <param name="stagger">The stagger value to set.</param>
void Stagger(T stagger)
{
m_Stagger = stagger;
}
private:
bool m_Smooth = true;
intmax_t m_SheepGen = -1;

View File

@ -2,6 +2,8 @@
#include "EmberAnimate.h"
#include "JpegUtils.h"
using namespace EmberCommon;
/// <summary>
/// The core of the EmberAnimate.exe program.
/// Template argument expected to be float or double.
@ -368,7 +370,7 @@ bool EmberAnimate(EmberOptions& opt)
break;
}
fnstream << inputPath << opt.Prefix() << setfill('0') << setw(padding) << ftime << opt.Suffix() << "." << opt.Format();
fnstream << inputPath << opt.Prefix() << setfill('0') << setprecision(0) << fixed << setw(padding) << ftime << opt.Suffix() << "." << opt.Format();
filename = fnstream.str();
fnstream.str("");

View File

@ -7,6 +7,8 @@
/// Ember and its derivatives.
/// </summary>
namespace EmberCommon
{
/// <summary>
/// Derivation of the RenderCallback class to do custom printing action
/// whenever the progress function is internally called inside of Ember
@ -147,6 +149,20 @@ static bool InitPaletteList(const string& filename)
return true;
}
/// <summary>
/// Formats a filename with digits using the passed in amount of 0 padding.
/// </summary>
/// <param name="result">The ember whose name will be set</param>
/// <param name="os">The ostringstream which will be used to format</param>
/// <param name="padding">The amount of padding to use</param>
template <typename T>
void FormatName(Ember<T>& result, ostringstream& os, streamsize padding)
{
os << std::setw(padding) << result.m_Time;
result.m_Name = os.str();
os.str("");
}
/// <summary>
/// Convert an RGBA buffer to an RGB buffer.
/// The two buffers can point to the same memory location if needed.
@ -162,7 +178,7 @@ static void RgbaToRgb(vector<byte>& rgba, vector<byte>& rgb, size_t width, size_
for (size_t i = 0, j = 0; i < (width * height * 4); i += 4, j += 3)
{
rgb[j] = rgba[i];
rgb[j] = rgba[i];
rgb[j + 1] = rgba[i + 1];
rgb[j + 2] = rgba[i + 2];
}
@ -371,7 +387,7 @@ static vector<unique_ptr<Renderer<T, float>>> CreateRenderers(eRendererType rend
else
{
s = "CPU";
v.push_back(std::move(unique_ptr<Renderer<T, float>>(::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
v.push_back(std::move(unique_ptr<Renderer<T, float>>(EmberCommon::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
}
}
catch (const std::exception& e)
@ -388,7 +404,7 @@ static vector<unique_ptr<Renderer<T, float>>> CreateRenderers(eRendererType rend
try
{
s = "CPU";
v.push_back(std::move(unique_ptr<Renderer<T, float>>(::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
v.push_back(std::move(unique_ptr<Renderer<T, float>>(EmberCommon::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
}
catch (const std::exception& e)
{
@ -628,6 +644,7 @@ static vector<const Variation<T>*> FindVarsWithout(const vector<const Variation<
return vec;
}
}
/// <summary>
/// Simple macro to print a string if the --verbose options has been specified.

View File

@ -87,6 +87,8 @@ enum class eOptionIDs : et
OPT_REPEAT,
OPT_TRIES,
OPT_MAX_XFORMS,
OPT_START_COUNT,
OPT_PADDING,
OPT_PRIORITY,
OPT_SS,//Float value args.
@ -307,52 +309,52 @@ public:
m_DoubleArgs.reserve(size);
m_StringArgs.reserve(size);
//Informational bools.
INITBOOLOPTION(Help, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_HELP, _T("--help"), false, SO_NONE, "\t--help Show this screen and exit.\n"));
INITBOOLOPTION(Version, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_VERSION, _T("--version"), false, SO_NONE, "\t--version Show version and exit.\n"));
INITBOOLOPTION(OpenCLInfo, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_DUMP_OPENCL_INFO, _T("--openclinfo"), false, SO_NONE, "\t--openclinfo Display platforms and devices for OpenCL and exit.\n"));
INITBOOLOPTION(AllVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ALL_VARS, _T("--allvars"), false, SO_NONE, "\t--allvars Display the names of all supported variations and exit.\n"));
INITBOOLOPTION(RegVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_REG_VARS, _T("--regvars"), false, SO_NONE, "\t--regvars Display the names of all supported regular variations and exit.\n"));
INITBOOLOPTION(PreVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PRE_VARS, _T("--prevars"), false, SO_NONE, "\t--prevars Display the names of all supported pre variations and exit.\n"));
INITBOOLOPTION(PostVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_POST_VARS, _T("--postvars"), false, SO_NONE, "\t--postvars Display the names of all supported post variations and exit.\n"));
INITBOOLOPTION(SumVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SUM_VARS, _T("--sumvars"), false, SO_NONE, "\t--sumvars Display the names of all regular variations which have the standard behavior of summing their output, and exit.\n"));
INITBOOLOPTION(AssignVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ASSIGN_VARS, _T("--assignvars"), false, SO_NONE, "\t--assignvars Display the names of all regular variations which have the non-standard behavior of assigning their output, and exit.\n"));
INITBOOLOPTION(PpSumVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PPSUM_VARS, _T("--ppsumvars"), false, SO_NONE, "\t--ppsumvars Display the names of all pre/post variations which have the non-standard behavior of summing their output, and exit.\n"));
INITBOOLOPTION(PpAssignVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PPASSIGN_VARS, _T("--ppassignvars"), false, SO_NONE, "\t--ppassignvars Display the names of all pre/post variations which have the standard behavior of assigning their output, and exit.\n"));
INITBOOLOPTION(DcVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_DC_VARS, _T("--dcvars"), false, SO_NONE, "\t--dcvars Display the names of all variations which alter the color index and exit.\n"));
INITBOOLOPTION(StateVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_STATE_VARS, _T("--statevars"), false, SO_NONE, "\t--statevars Display the names of all variations which alter their state on each iteration and exit.\n"));
INITBOOLOPTION(ParVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PAR_VARS, _T("--parvars"), false, SO_NONE, "\t--parvars Display the names of all variations which have parameters and exit.\n"));
INITBOOLOPTION(NonParVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_NON_PAR_VARS, _T("--nonparvars"), false, SO_NONE, "\t--nonparvars Display the names of all variations which do not have parameters (weight only) and exit.\n"));
INITBOOLOPTION(Help, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_HELP, _T("--help"), false, SO_NONE, "\t--help Show this screen and exit.\n"));
INITBOOLOPTION(Version, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_VERSION, _T("--version"), false, SO_NONE, "\t--version Show version and exit.\n"));
INITBOOLOPTION(OpenCLInfo, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_DUMP_OPENCL_INFO, _T("--openclinfo"), false, SO_NONE, "\t--openclinfo Display platforms and devices for OpenCL and exit.\n"));
INITBOOLOPTION(AllVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ALL_VARS, _T("--allvars"), false, SO_NONE, "\t--allvars Display the names of all supported variations and exit.\n"));
INITBOOLOPTION(RegVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_REG_VARS, _T("--regvars"), false, SO_NONE, "\t--regvars Display the names of all supported regular variations and exit.\n"));
INITBOOLOPTION(PreVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PRE_VARS, _T("--prevars"), false, SO_NONE, "\t--prevars Display the names of all supported pre variations and exit.\n"));
INITBOOLOPTION(PostVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_POST_VARS, _T("--postvars"), false, SO_NONE, "\t--postvars Display the names of all supported post variations and exit.\n"));
INITBOOLOPTION(SumVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SUM_VARS, _T("--sumvars"), false, SO_NONE, "\t--sumvars Display the names of all regular variations which have the standard behavior of summing their output, and exit.\n"));
INITBOOLOPTION(AssignVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ASSIGN_VARS, _T("--assignvars"), false, SO_NONE, "\t--assignvars Display the names of all regular variations which have the non-standard behavior of assigning their output, and exit.\n"));
INITBOOLOPTION(PpSumVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PPSUM_VARS, _T("--ppsumvars"), false, SO_NONE, "\t--ppsumvars Display the names of all pre/post variations which have the non-standard behavior of summing their output, and exit.\n"));
INITBOOLOPTION(PpAssignVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PPASSIGN_VARS, _T("--ppassignvars"), false, SO_NONE, "\t--ppassignvars Display the names of all pre/post variations which have the standard behavior of assigning their output, and exit.\n"));
INITBOOLOPTION(DcVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_DC_VARS, _T("--dcvars"), false, SO_NONE, "\t--dcvars Display the names of all variations which alter the color index and exit.\n"));
INITBOOLOPTION(StateVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_STATE_VARS, _T("--statevars"), false, SO_NONE, "\t--statevars Display the names of all variations which alter their state on each iteration and exit.\n"));
INITBOOLOPTION(ParVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PAR_VARS, _T("--parvars"), false, SO_NONE, "\t--parvars Display the names of all variations which have parameters and exit.\n"));
INITBOOLOPTION(NonParVars, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_NON_PAR_VARS, _T("--nonparvars"), false, SO_NONE, "\t--nonparvars Display the names of all variations which do not have parameters (weight only) and exit.\n"));
//Diagnostic bools.
INITBOOLOPTION(Verbose, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_VERBOSE, _T("--verbose"), false, SO_NONE, "\t--verbose Verbose output [default: false].\n"));
INITBOOLOPTION(Debug, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_DEBUG, _T("--debug"), false, SO_NONE, "\t--debug Debug output [default: false].\n"));
INITBOOLOPTION(DumpArgs, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_DUMP_ARGS, _T("--dumpargs"), false, SO_NONE, "\t--dumpargs Print all arguments entered from either the command line or environment variables [default: false].\n"));
INITBOOLOPTION(DoProgress, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PROGRESS, _T("--progress"), false, SO_NONE, "\t--progress Display progress. This will slow down processing by about 10% [default: false].\n"));
INITBOOLOPTION(Verbose, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_VERBOSE, _T("--verbose"), false, SO_NONE, "\t--verbose Verbose output [default: false].\n"));
INITBOOLOPTION(Debug, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_DEBUG, _T("--debug"), false, SO_NONE, "\t--debug Debug output [default: false].\n"));
INITBOOLOPTION(DumpArgs, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_DUMP_ARGS, _T("--dumpargs"), false, SO_NONE, "\t--dumpargs Print all arguments entered from either the command line or environment variables [default: false].\n"));
INITBOOLOPTION(DoProgress, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PROGRESS, _T("--progress"), false, SO_NONE, "\t--progress Display progress. This will slow down processing by about 10% [default: false].\n"));
//Execution bools.
INITBOOLOPTION(EmberCL, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_OPENCL, _T("--opencl"), false, SO_NONE, "\t--opencl Use OpenCL renderer (EmberCL) for rendering [default: false].\n"));
INITBOOLOPTION(Sp, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_SP, _T("--sp"), false, SO_NONE, "\t--sp Use single precision for rendering instead of double precision [default: false].\n"));
INITBOOLOPTION(EarlyClip, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_EARLYCLIP, _T("--earlyclip"), false, SO_NONE, "\t--earlyclip Perform clipping of RGB values before spatial filtering for better antialiasing and resizing [default: false].\n"));
INITBOOLOPTION(YAxisUp, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_POS_Y_UP, _T("--yaxisup"), false, SO_NONE, "\t--yaxisup Orient the image with the positive y axis pointing up [default: false].\n"));
INITBOOLOPTION(Transparency, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_TRANSPARENCY, _T("--transparency"), false, SO_NONE, "\t--transparency Include alpha channel in final output [default: false except for PNG].\n"));
INITBOOLOPTION(NameEnable, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_NAME_ENABLE, _T("--name_enable"), false, SO_NONE, "\t--name_enable Use the name attribute contained in the Xml as the output filename [default: false].\n"));
INITBOOLOPTION(HexPalette, Eob(eOptionUse::OPT_ANIM_GENOME, eOptionIDs::OPT_HEX_PALETTE, _T("--hex_palette"), true, SO_OPT, "\t--hex_palette Force palette RGB values to be hex when saving to Xml [default: true].\n"));
INITBOOLOPTION(InsertPalette, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_INSERT_PALETTE, _T("--insert_palette"), false, SO_NONE, "\t--insert_palette Insert the palette into the image for debugging purposes. Disabled when running with OpenCL [default: false].\n"));
INITBOOLOPTION(JpegComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_JPEG_COMMENTS, _T("--enable_jpg_comments"), false, SO_NONE, "\t--enable_jpg_comments Enables embedding the flame parameters and user identifying information in the jpeg header [default: false].\n"));
INITBOOLOPTION(PngComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PNG_COMMENTS, _T("--enable_png_comments"), false, SO_NONE, "\t--enable_png_comments Enables embedding the flame parameters and user identifying information in the png header [default: false].\n"));
INITBOOLOPTION(WriteGenome, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_WRITE_GENOME, _T("--write_genome"), false, SO_NONE, "\t--write_genome Write out flame associated with center of motion blur window [default: false].\n"));
INITBOOLOPTION(ThreadedWrite, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_THREADED_WRITE, _T("--threaded_write"), true, SO_OPT, "\t--threaded_write Use a separate thread to write images to disk. This gives better performance, but doubles the memory required for the final output buffer. [default: true].\n"));
INITBOOLOPTION(Enclosed, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ENCLOSED, _T("--enclosed"), true, SO_OPT, "\t--enclosed Use enclosing Xml tags [default: true].\n"));
INITBOOLOPTION(NoEdits, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_NO_EDITS, _T("--noedits"), false, SO_NONE, "\t--noedits Exclude edit tags when writing Xml [default: false].\n"));
INITBOOLOPTION(UnsmoothEdge, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_UNSMOOTH_EDGE, _T("--unsmoother"), false, SO_NONE, "\t--unsmoother Do not use smooth blending for sheep edges [default: false].\n"));
INITBOOLOPTION(LockAccum, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_LOCK_ACCUM, _T("--lock_accum"), false, SO_NONE, "\t--lock_accum Lock threads when accumulating to the histogram using the CPU. This will drop performance to that of single threading [default: false].\n"));
INITBOOLOPTION(DumpKernel, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_DUMP_KERNEL, _T("--dump_kernel"), false, SO_NONE, "\t--dump_kernel Print the iteration kernel string when using OpenCL (ignored for CPU) [default: false].\n"));
INITBOOLOPTION(EmberCL, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_OPENCL, _T("--opencl"), false, SO_NONE, "\t--opencl Use OpenCL renderer (EmberCL) for rendering [default: false].\n"));
INITBOOLOPTION(Sp, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_SP, _T("--sp"), false, SO_NONE, "\t--sp Use single precision for rendering instead of double precision [default: false].\n"));
INITBOOLOPTION(EarlyClip, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_EARLYCLIP, _T("--earlyclip"), false, SO_NONE, "\t--earlyclip Perform clipping of RGB values before spatial filtering for better antialiasing and resizing [default: false].\n"));
INITBOOLOPTION(YAxisUp, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_POS_Y_UP, _T("--yaxisup"), false, SO_NONE, "\t--yaxisup Orient the image with the positive y axis pointing up [default: false].\n"));
INITBOOLOPTION(Transparency, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_TRANSPARENCY, _T("--transparency"), false, SO_NONE, "\t--transparency Include alpha channel in final output [default: false except for PNG].\n"));
INITBOOLOPTION(NameEnable, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_NAME_ENABLE, _T("--name_enable"), false, SO_NONE, "\t--name_enable Use the name attribute contained in the Xml as the output filename [default: false].\n"));
INITBOOLOPTION(HexPalette, Eob(eOptionUse::OPT_ANIM_GENOME, eOptionIDs::OPT_HEX_PALETTE, _T("--hex_palette"), true, SO_OPT, "\t--hex_palette Force palette RGB values to be hex when saving to Xml [default: true].\n"));
INITBOOLOPTION(InsertPalette, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_INSERT_PALETTE, _T("--insert_palette"), false, SO_NONE, "\t--insert_palette Insert the palette into the image for debugging purposes. Disabled when running with OpenCL [default: false].\n"));
INITBOOLOPTION(JpegComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_JPEG_COMMENTS, _T("--enable_jpg_comments"), false, SO_NONE, "\t--enable_jpg_comments Enables embedding the flame parameters and user identifying information in the jpeg header [default: false].\n"));
INITBOOLOPTION(PngComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PNG_COMMENTS, _T("--enable_png_comments"), false, SO_NONE, "\t--enable_png_comments Enables embedding the flame parameters and user identifying information in the png header [default: false].\n"));
INITBOOLOPTION(WriteGenome, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_WRITE_GENOME, _T("--write_genome"), false, SO_NONE, "\t--write_genome Write out flame associated with center of motion blur window [default: false].\n"));
INITBOOLOPTION(ThreadedWrite, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_THREADED_WRITE, _T("--threaded_write"), true, SO_OPT, "\t--threaded_write Use a separate thread to write images to disk. This gives better performance, but doubles the memory required for the final output buffer. [default: true].\n"));
INITBOOLOPTION(Enclosed, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ENCLOSED, _T("--enclosed"), true, SO_OPT, "\t--enclosed Use enclosing Xml tags [default: true].\n"));
INITBOOLOPTION(NoEdits, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_NO_EDITS, _T("--noedits"), false, SO_NONE, "\t--noedits Exclude edit tags when writing Xml [default: false].\n"));
INITBOOLOPTION(UnsmoothEdge, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_UNSMOOTH_EDGE, _T("--unsmoother"), false, SO_NONE, "\t--unsmoother Do not use smooth blending for sheep edges [default: false].\n"));
INITBOOLOPTION(LockAccum, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_LOCK_ACCUM, _T("--lock_accum"), false, SO_NONE, "\t--lock_accum Lock threads when accumulating to the histogram using the CPU. This will drop performance to that of single threading [default: false].\n"));
INITBOOLOPTION(DumpKernel, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_DUMP_KERNEL, _T("--dump_kernel"), false, SO_NONE, "\t--dump_kernel Print the iteration kernel string when using OpenCL (ignored for CPU) [default: false].\n"));
//Int.
INITINTOPTION(Symmetry, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SYMMETRY, _T("--symmetry"), 0, SO_REQ_SEP, "\t--symmetry=<val> Set symmetry of result [default: 0].\n"));
INITINTOPTION(SheepGen, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SHEEP_GEN, _T("--sheep_gen"), -1, SO_REQ_SEP, "\t--sheep_gen=<val> Sheep generation of this flame [default: -1].\n"));
INITINTOPTION(SheepId, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SHEEP_ID, _T("--sheep_id"), -1, SO_REQ_SEP, "\t--sheep_id=<val> Sheep ID of this flame [default: -1].\n"));
INITINTOPTION(Symmetry, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SYMMETRY, _T("--symmetry"), 0, SO_REQ_SEP, "\t--symmetry=<val> Set symmetry of result [default: 0].\n"));
INITINTOPTION(SheepGen, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SHEEP_GEN, _T("--sheep_gen"), -1, SO_REQ_SEP, "\t--sheep_gen=<val> Sheep generation of this flame [default: -1].\n"));
INITINTOPTION(SheepId, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SHEEP_ID, _T("--sheep_id"), -1, SO_REQ_SEP, "\t--sheep_id=<val> Sheep ID of this flame [default: -1].\n"));
#ifdef _WIN32
INITINTOPTION(Priority, Eoi(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PRIORITY, _T("--priority"), int(eThreadPriority::NORMAL), SO_REQ_SEP, "\t--priority=<val> The priority of the CPU rendering threads from -2 - 2. This does not apply to OpenCL rendering.\n"));
INITINTOPTION(Priority, Eoi(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PRIORITY, _T("--priority"), int(eThreadPriority::NORMAL), SO_REQ_SEP, "\t--priority=<val> The priority of the CPU rendering threads from -2 - 2. This does not apply to OpenCL rendering.\n"));
#else
INITINTOPTION(Priority, Eoi(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PRIORITY, _T("--priority"), int(eThreadPriority::NORMAL), SO_REQ_SEP, "\t--priority=<val> The priority of the CPU rendering threads, 1, 25, 50, 75, 99. This does not apply to OpenCL rendering.\n"));
INITINTOPTION(Priority, Eoi(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PRIORITY, _T("--priority"), int(eThreadPriority::NORMAL), SO_REQ_SEP, "\t--priority=<val> The priority of the CPU rendering threads, 1, 25, 50, 75, 99. This does not apply to OpenCL rendering.\n"));
#endif
//Uint.
INITUINTOPTION(ThreadCount, Eou(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_NTHREADS, _T("--nthreads"), 0, SO_REQ_SEP, "\t--nthreads=<val> The number of threads to use [default: use all available cores].\n"));
@ -370,52 +372,54 @@ public:
INITUINTOPTION(Repeat, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat=<val> Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n"));
INITUINTOPTION(Tries, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries=<val> Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n"));
INITUINTOPTION(MaxXforms, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms=<val> The maximum number of xforms allowed in the final output.\n"));
INITUINTOPTION(StartCount, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_START_COUNT, _T("--startcount"), 0, SO_REQ_SEP, "\t--startcount=<val> The number to add to each flame name when generating a sequence. Useful for programs like ffmpeg which require numerically increasing filenames [default: 0].\n"));
INITUINTOPTION(Padding, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PADDING, _T("--padding"), 0, SO_REQ_SEP, "\t--padding=<val> Override the amount of zero padding added to each flame name when generating a sequence. Useful for programs like ffmpeg which require fixed width filenames [default: 0 (auto calculate padding)].\n"));
//Double.
INITDOUBLEOPTION(SizeScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SS, _T("--ss"), 1, SO_REQ_SEP, "\t--ss=<val> Size scale. All dimensions are scaled by this amount [default: 1.0].\n"));
INITDOUBLEOPTION(QualityScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_QS, _T("--qs"), 1, SO_REQ_SEP, "\t--qs=<val> Quality scale. All quality values are scaled by this amount [default: 1.0].\n"));
INITDOUBLEOPTION(Quality, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_QUALITY, _T("--quality"), 0, SO_REQ_SEP, "\t--quality=<val> Override the quality of the flame if not 0 [default: 0].\n"));
INITDOUBLEOPTION(DeMin, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_DE_MIN, _T("--demin"), -1, SO_REQ_SEP, "\t--demin=<val> Override the minimum size of the density estimator filter radius if not -1 [default: -1].\n"));
INITDOUBLEOPTION(DeMax, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_DE_MAX, _T("--demax"), -1, SO_REQ_SEP, "\t--demax=<val> Override the maximum size of the density estimator filter radius if not -1 [default: -1].\n"));
INITDOUBLEOPTION(AspectRatio, Eod(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PIXEL_ASPECT, _T("--pixel_aspect"), 1, SO_REQ_SEP, "\t--pixel_aspect=<val> Aspect ratio of pixels (width over height), eg. 0.90909 for NTSC [default: 1.0].\n"));
INITDOUBLEOPTION(Stagger, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_STAGGER, _T("--stagger"), 0, SO_REQ_SEP, "\t--stagger=<val> Affects simultaneity of xform interpolation during flame interpolation.\n"
INITDOUBLEOPTION(SizeScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SS, _T("--ss"), 1, SO_REQ_SEP, "\t--ss=<val> Size scale. All dimensions are scaled by this amount [default: 1.0].\n"));
INITDOUBLEOPTION(QualityScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_QS, _T("--qs"), 1, SO_REQ_SEP, "\t--qs=<val> Quality scale. All quality values are scaled by this amount [default: 1.0].\n"));
INITDOUBLEOPTION(Quality, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_QUALITY, _T("--quality"), 0, SO_REQ_SEP, "\t--quality=<val> Override the quality of the flame if not 0 [default: 0].\n"));
INITDOUBLEOPTION(DeMin, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_DE_MIN, _T("--demin"), -1, SO_REQ_SEP, "\t--demin=<val> Override the minimum size of the density estimator filter radius if not -1 [default: -1].\n"));
INITDOUBLEOPTION(DeMax, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_DE_MAX, _T("--demax"), -1, SO_REQ_SEP, "\t--demax=<val> Override the maximum size of the density estimator filter radius if not -1 [default: -1].\n"));
INITDOUBLEOPTION(AspectRatio, Eod(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PIXEL_ASPECT, _T("--pixel_aspect"), 1, SO_REQ_SEP, "\t--pixel_aspect=<val> Aspect ratio of pixels (width over height), eg. 0.90909 for NTSC [default: 1.0].\n"));
INITDOUBLEOPTION(Stagger, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_STAGGER, _T("--stagger"), 0, SO_REQ_SEP, "\t--stagger=<val> Affects simultaneity of xform interpolation during flame interpolation.\n"
"\t Represents how 'separate' the xforms are interpolated. Set to 1 for each\n"
"\t xform to be interpolated individually, fractions control interpolation overlap [default: 0].\n"));
INITDOUBLEOPTION(AvgThresh, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_AVG_THRESH, _T("--avg"), 20.0, SO_REQ_SEP, "\t--avg=<val> Minimum average pixel channel sum (r + g + b) threshold from 0 - 765. Ignored if sequence, inter or rotate were specified [default: 20].\n"));
INITDOUBLEOPTION(BlackThresh, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_BLACK_THRESH, _T("--black"), 0.01, SO_REQ_SEP, "\t--black=<val> Minimum number of allowed black pixels as a percentage from 0 - 1. Ignored if sequence, inter or rotate were specified [default: 0.01].\n"));
INITDOUBLEOPTION(WhiteLimit, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_WHITE_LIMIT, _T("--white"), 0.05, SO_REQ_SEP, "\t--white=<val> Maximum number of allowed white pixels as a percentage from 0 - 1. Ignored if sequence, inter or rotate were specified [default: 0.05].\n"));
INITDOUBLEOPTION(Speed, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SPEED, _T("--speed"), 0.1, SO_REQ_SEP, "\t--speed=<val> Speed as a percentage from 0 - 1 that the affine transform of an existing flame mutates with the new flame. Ignored if sequence, inter or rotate were specified [default: 0.1].\n"));
INITDOUBLEOPTION(OffsetX, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_OFFSETX, _T("--offsetx"), 0.0, SO_REQ_SEP, "\t--offsetx=<val> Amount to jitter each flame horizontally when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(OffsetY, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_OFFSETY, _T("--offsety"), 0.0, SO_REQ_SEP, "\t--offsety=<val> Amount to jitter each flame vertically when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(UseMem, Eod(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_USEMEM, _T("--use_mem"), 0.0, SO_REQ_SEP, "\t--use_mem=<val> Number of bytes of memory to use [default: max system memory].\n"));
INITDOUBLEOPTION(Loops, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_LOOPS, _T("--loops"), 1.0, SO_REQ_SEP, "\t--loops=<val> Number of times to rotate each control point in sequence [default: 1].\n"));
INITDOUBLEOPTION(AvgThresh, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_AVG_THRESH, _T("--avg"), 20.0, SO_REQ_SEP, "\t--avg=<val> Minimum average pixel channel sum (r + g + b) threshold from 0 - 765. Ignored if sequence, inter or rotate were specified [default: 20].\n"));
INITDOUBLEOPTION(BlackThresh, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_BLACK_THRESH, _T("--black"), 0.01, SO_REQ_SEP, "\t--black=<val> Minimum number of allowed black pixels as a percentage from 0 - 1. Ignored if sequence, inter or rotate were specified [default: 0.01].\n"));
INITDOUBLEOPTION(WhiteLimit, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_WHITE_LIMIT, _T("--white"), 0.05, SO_REQ_SEP, "\t--white=<val> Maximum number of allowed white pixels as a percentage from 0 - 1. Ignored if sequence, inter or rotate were specified [default: 0.05].\n"));
INITDOUBLEOPTION(Speed, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SPEED, _T("--speed"), 0.1, SO_REQ_SEP, "\t--speed=<val> Speed as a percentage from 0 - 1 that the affine transform of an existing flame mutates with the new flame. Ignored if sequence, inter or rotate were specified [default: 0.1].\n"));
INITDOUBLEOPTION(OffsetX, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_OFFSETX, _T("--offsetx"), 0.0, SO_REQ_SEP, "\t--offsetx=<val> Amount to jitter each flame horizontally when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(OffsetY, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_OFFSETY, _T("--offsety"), 0.0, SO_REQ_SEP, "\t--offsety=<val> Amount to jitter each flame vertically when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(UseMem, Eod(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_USEMEM, _T("--use_mem"), 0.0, SO_REQ_SEP, "\t--use_mem=<val> Number of bytes of memory to use [default: max system memory].\n"));
INITDOUBLEOPTION(Loops, Eod(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_LOOPS, _T("--loops"), 1.0, SO_REQ_SEP, "\t--loops=<val> Number of times to rotate each control point in sequence [default: 1].\n"));
//String.
INITSTRINGOPTION(Device, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_OPENCL_DEVICE, _T("--device"), "0", SO_REQ_SEP, "\t--device The comma-separated OpenCL device indices to use. Single device: 0 Multi device: 0,1,3,4 [default: 0].\n"));
INITSTRINGOPTION(IsaacSeed, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_ISAAC_SEED, _T("--isaac_seed"), "", SO_REQ_SEP, "\t--isaac_seed=<val> Character-based seed for the random number generator [default: random].\n"));
INITSTRINGOPTION(Input, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_IN, _T("--in"), "", SO_REQ_SEP, "\t--in=<val> Name of the input file.\n"));
INITSTRINGOPTION(Out, Eos(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_OUT, _T("--out"), "", SO_REQ_SEP, "\t--out=<val> Name of a single output file. Not recommended when rendering more than one image.\n"));
INITSTRINGOPTION(Prefix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PREFIX, _T("--prefix"), "", SO_REQ_SEP, "\t--prefix=<val> Prefix to prepend to all output files.\n"));
INITSTRINGOPTION(Suffix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SUFFIX, _T("--suffix"), "", SO_REQ_SEP, "\t--suffix=<val> Suffix to append to all output files.\n"));
INITSTRINGOPTION(Format, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_FORMAT, _T("--format"), "png", SO_REQ_SEP, "\t--format=<val> Format of the output file. Valid values are: bmp, jpg, png, ppm [default: png].\n"));
INITSTRINGOPTION(PalettePath, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PALETTE_FILE, _T("--flam3_palettes"), "flam3-palettes.xml", SO_REQ_SEP, "\t--flam3_palettes=<val> Path and name of the palette file [default: flam3-palettes.xml].\n"));
INITSTRINGOPTION(Id, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_ID, _T("--id"), "", SO_REQ_SEP, "\t--id=<val> ID to use in <edit> tags / image comments.\n"));
INITSTRINGOPTION(Url, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_URL, _T("--url"), "", SO_REQ_SEP, "\t--url=<val> URL to use in <edit> tags / image comments.\n"));
INITSTRINGOPTION(Nick, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_NICK, _T("--nick"), "", SO_REQ_SEP, "\t--nick=<val> Nickname to use in <edit> tags / image comments.\n"));
INITSTRINGOPTION(Comment, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_COMMENT, _T("--comment"), "", SO_REQ_SEP, "\t--comment=<val> Comment to use in <edit> tags.\n"));
INITSTRINGOPTION(TemplateFile, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_TEMPLATE, _T("--template"), "", SO_REQ_SEP, "\t--template=<val> Apply defaults based on this flame.\n"));
INITSTRINGOPTION(Clone, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE, _T("--clone"), "", SO_REQ_SEP, "\t--clone=<val> Clone random flame in input.\n"));
INITSTRINGOPTION(CloneAll, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE_ALL, _T("--clone_all"), "", SO_REQ_SEP, "\t--clone_all=<val> Clones all flames in the input file. Useful for applying template to all flames.\n"));
INITSTRINGOPTION(CloneAction, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE_ACTION, _T("--clone_action"), "", SO_REQ_SEP, "\t--clone_action=<val> A description of the clone action taking place.\n"));
INITSTRINGOPTION(Animate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ANIMATE, _T("--animate"), "", SO_REQ_SEP, "\t--animate=<val> Interpolates between all flames in the input file, using times specified in file.\n"));
INITSTRINGOPTION(Mutate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_MUTATE, _T("--mutate"), "", SO_REQ_SEP, "\t--mutate=<val> Randomly mutate a random flame from the input file.\n"));
INITSTRINGOPTION(Cross0, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CROSS0, _T("--cross0"), "", SO_REQ_SEP, "\t--cross0=<val> Randomly select one flame from the input file to genetically cross...\n"));
INITSTRINGOPTION(Cross1, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CROSS1, _T("--cross1"), "", SO_REQ_SEP, "\t--cross1=<val> ...with one flame from this file.\n"));
INITSTRINGOPTION(Method, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_METHOD, _T("--method"), "", SO_REQ_SEP, "\t--method=<val> Method used for genetic cross: alternate, interpolate, or union. For mutate: all_vars, one_xform, add_symmetry, post_xforms, color_palette, delete_xform, all_coefs [default: random].\n"));//Original ommitted this important documentation for mutate!
INITSTRINGOPTION(Inter, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_INTER, _T("--inter"), "", SO_REQ_SEP, "\t--inter=<val> Interpolate the input file.\n"));
INITSTRINGOPTION(Rotate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ROTATE, _T("--rotate"), "", SO_REQ_SEP, "\t--rotate=<val> Rotate the input file.\n"));
INITSTRINGOPTION(Sequence, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SEQUENCE, _T("--sequence"), "", SO_REQ_SEP, "\t--sequence=<val> 360 degree rotation 'loops' times of each control point in the input file plus rotating transitions.\n"));
INITSTRINGOPTION(UseVars, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_USE_VARS, _T("--use_vars"), "", SO_REQ_SEP, "\t--use_vars=<val> Comma separated list of variation #'s to use when generating a random flame.\n"));
INITSTRINGOPTION(DontUseVars, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_DONT_USE_VARS, _T("--dont_use_vars"), "", SO_REQ_SEP, "\t--dont_use_vars=<val> Comma separated list of variation #'s to NOT use when generating a random flame.\n"));
INITSTRINGOPTION(Extras, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_EXTRAS, _T("--extras"), "", SO_REQ_SEP, "\t--extras=<val> Extra attributes to place in the flame section of the Xml.\n"));
INITSTRINGOPTION(Device, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_OPENCL_DEVICE, _T("--device"), "0", SO_REQ_SEP, "\t--device The comma-separated OpenCL device indices to use. Single device: 0 Multi device: 0,1,3,4 [default: 0].\n"));
INITSTRINGOPTION(IsaacSeed, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_ISAAC_SEED, _T("--isaac_seed"), "", SO_REQ_SEP, "\t--isaac_seed=<val> Character-based seed for the random number generator [default: random].\n"));
INITSTRINGOPTION(Input, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_IN, _T("--in"), "", SO_REQ_SEP, "\t--in=<val> Name of the input file.\n"));
INITSTRINGOPTION(Out, Eos(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_OUT, _T("--out"), "", SO_REQ_SEP, "\t--out=<val> Name of a single output file. Not recommended when rendering more than one image.\n"));
INITSTRINGOPTION(Prefix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PREFIX, _T("--prefix"), "", SO_REQ_SEP, "\t--prefix=<val> Prefix to prepend to all output files.\n"));
INITSTRINGOPTION(Suffix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SUFFIX, _T("--suffix"), "", SO_REQ_SEP, "\t--suffix=<val> Suffix to append to all output files.\n"));
INITSTRINGOPTION(Format, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_FORMAT, _T("--format"), "png", SO_REQ_SEP, "\t--format=<val> Format of the output file. Valid values are: bmp, jpg, png, ppm [default: png].\n"));
INITSTRINGOPTION(PalettePath, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PALETTE_FILE, _T("--flam3_palettes"), "flam3-palettes.xml", SO_REQ_SEP, "\t--flam3_palettes=<val> Path and name of the palette file [default: flam3-palettes.xml].\n"));
INITSTRINGOPTION(Id, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_ID, _T("--id"), "", SO_REQ_SEP, "\t--id=<val> ID to use in <edit> tags / image comments.\n"));
INITSTRINGOPTION(Url, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_URL, _T("--url"), "", SO_REQ_SEP, "\t--url=<val> URL to use in <edit> tags / image comments.\n"));
INITSTRINGOPTION(Nick, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_NICK, _T("--nick"), "", SO_REQ_SEP, "\t--nick=<val> Nickname to use in <edit> tags / image comments.\n"));
INITSTRINGOPTION(Comment, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_COMMENT, _T("--comment"), "", SO_REQ_SEP, "\t--comment=<val> Comment to use in <edit> tags.\n"));
INITSTRINGOPTION(TemplateFile, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_TEMPLATE, _T("--template"), "", SO_REQ_SEP, "\t--template=<val> Apply defaults based on this flame.\n"));
INITSTRINGOPTION(Clone, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE, _T("--clone"), "", SO_REQ_SEP, "\t--clone=<val> Clone random flame in input.\n"));
INITSTRINGOPTION(CloneAll, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE_ALL, _T("--clone_all"), "", SO_REQ_SEP, "\t--clone_all=<val> Clones all flames in the input file. Useful for applying template to all flames.\n"));
INITSTRINGOPTION(CloneAction, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE_ACTION, _T("--clone_action"), "", SO_REQ_SEP, "\t--clone_action=<val> A description of the clone action taking place.\n"));
INITSTRINGOPTION(Animate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ANIMATE, _T("--animate"), "", SO_REQ_SEP, "\t--animate=<val> Interpolates between all flames in the input file, using times specified in file.\n"));
INITSTRINGOPTION(Mutate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_MUTATE, _T("--mutate"), "", SO_REQ_SEP, "\t--mutate=<val> Randomly mutate a random flame from the input file.\n"));
INITSTRINGOPTION(Cross0, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CROSS0, _T("--cross0"), "", SO_REQ_SEP, "\t--cross0=<val> Randomly select one flame from the input file to genetically cross...\n"));
INITSTRINGOPTION(Cross1, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CROSS1, _T("--cross1"), "", SO_REQ_SEP, "\t--cross1=<val> ...with one flame from this file.\n"));
INITSTRINGOPTION(Method, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_METHOD, _T("--method"), "", SO_REQ_SEP, "\t--method=<val> Method used for genetic cross: alternate, interpolate, or union. For mutate: all_vars, one_xform, add_symmetry, post_xforms, color_palette, delete_xform, all_coefs [default: random].\n"));//Original ommitted this important documentation for mutate!
INITSTRINGOPTION(Inter, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_INTER, _T("--inter"), "", SO_REQ_SEP, "\t--inter=<val> Interpolate the input file.\n"));
INITSTRINGOPTION(Rotate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ROTATE, _T("--rotate"), "", SO_REQ_SEP, "\t--rotate=<val> Rotate the input file.\n"));
INITSTRINGOPTION(Sequence, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SEQUENCE, _T("--sequence"), "", SO_REQ_SEP, "\t--sequence=<val> 360 degree rotation 'loops' times of each control point in the input file plus rotating transitions.\n"));
INITSTRINGOPTION(UseVars, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_USE_VARS, _T("--use_vars"), "", SO_REQ_SEP, "\t--use_vars=<val> Comma separated list of variation #'s to use when generating a random flame.\n"));
INITSTRINGOPTION(DontUseVars, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_DONT_USE_VARS, _T("--dont_use_vars"), "", SO_REQ_SEP, "\t--dont_use_vars=<val> Comma separated list of variation #'s to NOT use when generating a random flame.\n"));
INITSTRINGOPTION(Extras, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_EXTRAS, _T("--extras"), "", SO_REQ_SEP, "\t--extras=<val> Extra attributes to place in the flame section of the Xml.\n"));
}
/// <summary>
@ -509,6 +513,8 @@ public:
PARSEUINTOPTION(eOptionIDs::OPT_REPEAT, Repeat);
PARSEUINTOPTION(eOptionIDs::OPT_TRIES, Tries);
PARSEUINTOPTION(eOptionIDs::OPT_MAX_XFORMS, MaxXforms);
PARSEUINTOPTION(eOptionIDs::OPT_START_COUNT, StartCount);
PARSEUINTOPTION(eOptionIDs::OPT_PADDING, Padding);
PARSEDOUBLEOPTION(eOptionIDs::OPT_SS, SizeScale);//Float args.
PARSEDOUBLEOPTION(eOptionIDs::OPT_QS, QualityScale);
PARSEDOUBLEOPTION(eOptionIDs::OPT_QUALITY, Quality);
@ -792,6 +798,8 @@ public:
Eou Repeat;
Eou Tries;
Eou MaxXforms;
Eou StartCount;
Eou Padding;
Eod SizeScale;//Value double.
Eod QualityScale;

View File

@ -3,6 +3,8 @@
#include "EmberGenome.h"
#include "JpegUtils.h"
using namespace EmberCommon;
/// <summary>
/// Set various default test values on the passed in ember.
/// </summary>
@ -34,14 +36,6 @@ void SetDefaultTestValues(Ember<T>& ember)
ember.m_CurveDE = T(0.6);
}
template <typename T>
void FormatName(Ember<T>& result, ostringstream& os, streamsize padding)
{
os << std::setw(padding) << result.m_Time;
result.m_Name = os.str();
os.str("");
}
/// <summary>
/// The core of the EmberGenome.exe program.
/// Template argument expected to be float or double.
@ -470,18 +464,20 @@ bool EmberGenome(EmberOptions& opt)
frameCount = 0;
os.str("");
os << setfill('0');
auto padding = streamsize(std::log10(((opt.Frames() * opt.Loops()) + opt.Frames()) * embers.size())) + 1;
os << setfill('0') << setprecision(0) << fixed;
auto padding = opt.Padding() ? streamsize(opt.Padding()) : (streamsize(std::log10(opt.StartCount() + (((opt.Frames() * opt.Loops()) + opt.Frames()) * embers.size()))) + 1);
t.Tic();
for (i = 0; i < embers.size(); i++)
{
if (opt.Loops() > 0)
{
for (frame = 0; frame < std::round(opt.Frames() * opt.Loops()); frame++)
size_t roundFrames = size_t(std::round(opt.Frames() * opt.Loops()));
for (frame = 0; frame < roundFrames; frame++)
{
blend = T(frame) / T(opt.Frames());
tools.Spin(embers[i], pTemplate, result, frameCount++, blend);//Result is cleared and reassigned each time inside of Spin().
tools.Spin(embers[i], pTemplate, result, opt.StartCount() + frameCount++, blend);//Result is cleared and reassigned each time inside of Spin().
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
@ -489,9 +485,9 @@ bool EmberGenome(EmberOptions& opt)
//The loop above will have rotated just shy of a complete rotation.
//Rotate the next step and save in result, but do not print.
//result will be the starting point for the interp phase below.
frame = size_t(std::round(opt.Frames() * opt.Loops()));
frame = roundFrames;
blend = T(frame) / T(opt.Frames());
tools.Spin(embers[i], pTemplate, result, frameCount, blend);//Do not increment frameCount here.
tools.Spin(embers[i], pTemplate, result, opt.StartCount() + frameCount, blend);//Do not increment frameCount here.
FormatName(result, os, padding);
}
@ -502,18 +498,17 @@ bool EmberGenome(EmberOptions& opt)
for (frame = 0; frame < opt.Frames(); frame++)
{
seqFlag = (frame == 0 || (frame == opt.Frames() - 1));
seqFlag = frame == 0 || (frame == opt.Frames() - 1);
blend = frame / T(opt.Frames());
result.Clear();
tools.SpinInter(&embers[i], pTemplate, result, frameCount++, seqFlag, blend);
tools.SpinInter(&embers[i], pTemplate, result, opt.StartCount() + frameCount++, seqFlag, blend);
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
}
}
result = embers.back();
tools.Spin(embers.back(), pTemplate, result, frameCount, 0);
tools.Spin(embers.back(), pTemplate, result, opt.StartCount() + frameCount, 0);
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
t.Toc("Sequencing");

View File

@ -2,7 +2,7 @@
#include "EmberRender.h"
#include "JpegUtils.h"
//template <class OpenCLInfo> weak_ptr<OpenCLInfo> Singleton<OpenCLInfo>::m_Instance = weak_ptr<OpenCLInfo>();
using namespace EmberCommon;
/// <summary>
/// The core of the EmberRender.exe program.
@ -273,7 +273,7 @@ bool EmberRender(EmberOptions& opt)
else
{
ostringstream fnstream;
fnstream << inputPath << opt.Prefix() << setfill('0') << setw(padding) << i << opt.Suffix() << "." << opt.Format();
fnstream << inputPath << opt.Prefix() << setfill('0') << setprecision(0) << fixed << setw(padding) << i << opt.Suffix() << "." << opt.Format();
filename = fnstream.str();
}

View File

@ -13,6 +13,7 @@
/// </summary>
using namespace EmberNs;
using namespace EmberCommon;
template <typename T>
void SaveFinalImage(Renderer<T, T>& renderer, vector<byte>& pixels, char* suffix)

View File

@ -159,9 +159,9 @@ public:
/// Return the default filename based on the current date/time.
/// </summary>
/// <returns>The default filename</returns>
static QString DefaultFilename()
static QString DefaultFilename(QString prefix = "Flame_")
{
return "Flame_" + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss");
return prefix + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss");
}
/// <summary>

View File

@ -28,7 +28,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
connect(ui.FinalRenderDoublePrecisionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoublePrecisionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderDoAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoAllCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderDoSequenceCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoSequenceCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderCurrentSpin, SIGNAL(valueChanged(int)), this, SLOT(OnCurrentSpinChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderCurrentSpin, SIGNAL(valueChanged(int)), this, SLOT(OnCurrentSpinChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderApplyToAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnApplyAllCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderKeepAspectCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnKeepAspectCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderScaleNoneRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
@ -189,6 +189,16 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
w = SetTabOrder(this, w, ui.FinalRenderCloseButton);
}
/// <summary>
/// Show the final render dialog and specify whether it was called from the toolbar or the sequence render button.
/// </summary>
/// <param name="fromSequence">True if this is called from the sequence render button, else false.</param>
void FractoriumFinalRenderDialog::Show(bool fromSequence)
{
m_FromSequence = fromSequence;
show();
}
/// <summary>
/// GUI settings wrapper functions, getters only.
/// </summary>
@ -636,7 +646,7 @@ void FractoriumFinalRenderDialog::OnCancelRenderClicked(bool checked)
/// <param name="e">The event</param>
void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
{
if (m_Controller.get() && m_Controller->m_Run)
if (m_Controller.get() && m_Controller->m_Run)//On Linux, this event will be called when the main window minimized/maximized while rendering, so filter it out.
return;
if (CreateControllerFromGUI(true))//Create controller if it does not exist, or if it does and the renderer is not running.
@ -645,16 +655,17 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
#ifdef DO_DOUBLE
Ember<double> ed;
EmberFile<double> efi;
m_Fractorium->m_Controller->CopyEmberFile(efi, [&](Ember<double>& ember)
m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember<double>& ember)
{
ember.SyncSize();
ember.m_Quality = m_Settings->FinalQuality();
ember.m_Supersample = m_Settings->FinalSupersample();
ember.m_TemporalSamples = m_Settings->FinalTemporalSamples();
});//Copy the whole file, will take about 0.2ms per ember in the file.
#else
Ember<float> ed;
EmberFile<float> efi;
m_Fractorium->m_Controller->CopyEmberFile(efi, [&](Ember<float>& ember)
m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember<float>& ember)
{
ember.SyncSize();
ember.m_Quality = m_Settings->FinalQuality();
@ -662,7 +673,7 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
ember.m_TemporalSamples = m_Settings->FinalTemporalSamples();
});//Copy the whole file, will take about 0.2ms per ember in the file.
#endif
m_Controller->SetEmberFile(efi);//Copy the temp file into the final render controller.
m_Controller->SetEmberFile(efi, true);//Move the temp file into the final render controller.
ui.FinalRenderCurrentSpin->setMaximum(int(efi.Size()));
ui.FinalRenderCurrentSpin->blockSignals(true);
ui.FinalRenderCurrentSpin->setValue(index);//Set the currently selected ember to the one that was being edited.
@ -677,6 +688,12 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
Path(m_Controller->ComposePath(m_Controller->Name()));//Update the GUI.
}
if (m_FromSequence)
{
ui.FinalRenderDoAllCheckBox->setChecked(true);
ui.FinalRenderDoSequenceCheckBox->setChecked(true);
}
ui.FinalRenderTextOutput->clear();
QDialog::showEvent(e);
}
@ -726,9 +743,9 @@ bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer)
if (m_Controller.get())
{
#ifdef DO_DOUBLE
m_Controller->CopyEmberFile(efd, [&](Ember<double>& ember) { });//Convert float to double or save double verbatim;
m_Controller->CopyEmberFile(efd, false, [&](Ember<double>& ember) { });//Convert float to double or save double verbatim;
#else
m_Controller->CopyEmberFile(efd, [&](Ember<float>& ember) { });//Convert float to double or save double verbatim;
m_Controller->CopyEmberFile(efd, false, [&](Ember<float>& ember) { });//Convert float to double or save double verbatim;
#endif
m_Controller->Shutdown();
}
@ -745,7 +762,7 @@ bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer)
//Restore the ember and ember file.
if (m_Controller.get())
{
m_Controller->SetEmberFile(efd);//Convert float to double or set double verbatim;
m_Controller->SetEmberFile(efd, true);//Convert float to double and move or move double verbatim.
m_Controller->SetEmber(index, false);
}
}

View File

@ -40,13 +40,18 @@ class FractoriumFinalRenderDialog : public QDialog
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(FractoriumSettings* settings, QWidget* p, Qt::WindowFlags f = 0);
void Show(bool fromSequence);
bool EarlyClip();
bool YAxisUp();
bool Transparency();
@ -113,6 +118,7 @@ private:
bool CreateControllerFromGUI(bool createRenderer);
bool SetMemory();
bool m_FromSequence;
int m_MemoryCellIndex;
int m_ItersCellIndex;
int m_PathCellIndex;

View File

@ -101,50 +101,7 @@ template<typename T>
FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender)
: FinalRenderEmberControllerBase(finalRender)
{
m_FinalPreviewRenderer = make_unique<EmberNs::Renderer<T, float>>();
m_FinalPreviewRenderer->Callback(nullptr);
m_FinalPreviewRenderer->NumChannels(4);
m_FinalPreviewRenderFunc = [&]()
{
rlg l(m_PreviewCs);//Thread prep.
m_PreviewRun = true;
m_FinalPreviewRenderer->Abort();
T scalePercentage;
size_t maxDim = 100;
QLabel* widget = m_FinalRenderDialog->ui.FinalRenderPreviewLabel;
//Determine how to scale the scaled ember to fit in the label with a max of 100x100.
if (m_Ember->m_FinalRasW >= m_Ember->m_FinalRasH)
scalePercentage = T(maxDim) / m_Ember->m_FinalRasW;
else
scalePercentage = T(maxDim) / m_Ember->m_FinalRasH;
m_PreviewEmber = *m_Ember;
m_PreviewEmber.m_Quality = 100;
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_FinalRasW = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasW)));//Ensure neither is zero.
m_PreviewEmber.m_FinalRasH = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasH)));
m_PreviewEmber.m_PixelsPerUnit = scalePercentage * m_Ember->m_PixelsPerUnit;
m_FinalPreviewRenderer->EarlyClip(m_FinalRenderDialog->EarlyClip());
m_FinalPreviewRenderer->YAxisUp(m_FinalRenderDialog->YAxisUp());
m_FinalPreviewRenderer->Transparency(m_FinalRenderDialog->Transparency());
m_FinalPreviewRenderer->SetEmber(m_PreviewEmber);
m_FinalPreviewRenderer->PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, m_FinalRenderDialog->Strips(),
[&](const string & s) { }, [&](const string & s) { }, [&](const string & s) { });
StripsRender<T>(m_FinalPreviewRenderer.get(), m_PreviewEmber, m_PreviewFinalImage, 0, strips, m_FinalRenderDialog->YAxisUp(),
[&](size_t strip) { },//Pre strip.
[&](size_t strip) { },//Post strip.
[&](size_t strip) { },//Error.
[&](Ember<T>& finalEmber)//Final strip.
{
QImage image(int(finalEmber.m_FinalRasW), int(finalEmber.m_FinalRasH), QImage::Format_RGBA8888);//The label wants RGBA.
memcpy(image.scanLine(0), m_PreviewFinalImage.data(), finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4);//Memcpy the data in.
QPixmap pixmap(QPixmap::fromImage(image));
QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap));
});
m_PreviewRun = false;
};
m_FinalPreviewRenderer = make_unique<FinalRenderPreviewRenderer<T>>(this);
//The main rendering function which will be called in a Qt thread.
//A backup Xml is made before the rendering process starts just in case it crashes before finishing.
//If it finishes successfully, delete the backup file.
@ -380,24 +337,24 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
/// These are used to preserve the current ember/file when switching between renderers.
/// Note that some precision will be lost when going from double to float.
/// </summary>
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile)
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile, bool move)
{
m_EmberFile = emberFile;
move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile;
m_Ember = m_EmberFile.Get(0);
}
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation)
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation)
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
#ifdef DO_DOUBLE
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile)
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile, bool move)
{
m_EmberFile = emberFile;
move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile;
m_Ember = m_EmberFile.Get(0);
}
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation)
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation)
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
@ -692,8 +649,7 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
m_Renderer->ComputeBounds();
m_Renderer->ComputeQuality();
m_Renderer->ComputeCamera();
CancelPreviewRender();
m_FinalPreviewRenderFunc();
m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX);
p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence());
iterCount = m_Renderer->TotalIterCount(strips);
}
@ -710,8 +666,7 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
renderer->ComputeCamera();
}
CancelPreviewRender();
m_FinalPreviewRenderFunc();
m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX);
strips = 1;
p = m_Renderers[0]->MemoryRequired(1, true, m_FinalRenderDialog->DoSequence());
iterCount = m_Renderers[0]->TotalIterCount(strips);
@ -759,22 +714,6 @@ EmberNs::Renderer<T, float>* FinalRenderEmberController<T>::FirstOrDefaultRender
}
}
/// <summary>
/// Stop the preview renderer.
/// This is meant to only be called programatically and never by the user.
/// </summary>
template <typename T>
void FinalRenderEmberController<T>::CancelPreviewRender()
{
m_FinalPreviewRenderer->Abort();
while (m_FinalPreviewRenderer->InRender()) { QApplication::processEvents(); }
while (m_PreviewRun) { QApplication::processEvents(); }
while (m_FinalPreviewResult.isRunning()) { QApplication::processEvents(); }
}
/// <summary>
/// Save the output of the render.
/// </summary>
@ -1017,6 +956,56 @@ QString FinalRenderEmberController<T>::CheckMemory(const tuple<size_t, size_t, s
return s;
}
/// <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>
/// <param name="start">Ignored</param>
/// <param name="end">Ignored</param>
template <typename T>
void FinalRenderPreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
{
T scalePercentage;
size_t maxDim = 100;
auto d = m_Controller->m_FinalRenderDialog;
QLabel* widget = d->ui.FinalRenderPreviewLabel;
//Determine how to scale the scaled ember to fit in the label with a max of 100x100.
auto e = m_Controller->m_Ember;
if (e->m_FinalRasW >= e->m_FinalRasH)
scalePercentage = T(maxDim) / e->m_FinalRasW;
else
scalePercentage = T(maxDim) / e->m_FinalRasH;
m_PreviewEmber = *e;
m_PreviewEmber.m_Quality = 100;
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_FinalRasW = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * e->m_FinalRasW)));//Ensure neither is zero.
m_PreviewEmber.m_FinalRasH = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * e->m_FinalRasH)));
m_PreviewEmber.m_PixelsPerUnit = scalePercentage * e->m_PixelsPerUnit;
m_PreviewRenderer.EarlyClip(d->EarlyClip());
m_PreviewRenderer.YAxisUp(d->YAxisUp());
m_PreviewRenderer.Transparency(d->Transparency());
m_PreviewRenderer.Callback(nullptr);
m_PreviewRenderer.NumChannels(4);
m_PreviewRenderer.SetEmber(m_PreviewEmber);
m_PreviewRenderer.PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, d->Strips(),
[&](const string & s) {}, [&](const string & s) {}, [&](const string & s) {});
StripsRender<T>(&m_PreviewRenderer, m_PreviewEmber, m_PreviewFinalImage, 0, strips, d->YAxisUp(),
[&](size_t strip) {},//Pre strip.
[&](size_t strip) {},//Post strip.
[&](size_t strip) {},//Error.
[&](Ember<T>& finalEmber)//Final strip.
{
QImage image(int(finalEmber.m_FinalRasW), int(finalEmber.m_FinalRasH), QImage::Format_RGBA8888);//The label wants RGBA.
memcpy(image.scanLine(0), m_PreviewFinalImage.data(), finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4);//Memcpy the data in.
QPixmap pixmap(QPixmap::fromImage(image));
QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap));
});
}
template class FinalRenderEmberController<float>;
#ifdef DO_DOUBLE

View File

@ -13,6 +13,7 @@
/// </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.
@ -74,19 +75,16 @@ public:
protected:
bool m_Run = false;
bool m_PreviewRun = false;
size_t m_ImageCount = 0;
std::atomic<size_t> m_FinishedImageCount;
QFuture<void> m_Result;
QFuture<void> m_FinalPreviewResult;
std::function<void (void)> m_FinalRenderFunc;
std::function<void (void)> m_FinalPreviewRenderFunc;
FractoriumSettings* m_Settings;
FractoriumFinalRenderDialog* m_FinalRenderDialog;
FinalRenderGuiState m_GuiState;
std::recursive_mutex m_PreviewCs, m_ProgressCs;
std::recursive_mutex m_ProgressCs;
Timing m_RenderTimer;
Timing m_TotalTimer;
};
@ -98,16 +96,18 @@ protected:
template<typename T>
class FinalRenderEmberController : public FinalRenderEmberControllerBase
{
friend FinalRenderPreviewRenderer<T>;
public:
FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender);
virtual ~FinalRenderEmberController() { }
//Virtual functions overridden from FractoriumEmberControllerBase.
virtual void SetEmberFile(const EmberFile<float>& emberFile) override;
virtual void CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<float>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
#ifdef DO_DOUBLE
virtual void SetEmberFile(const EmberFile<double>& emberFile) override;
virtual void CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<double>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
#endif
virtual void SetEmber(size_t index, bool verbatim) override;
virtual bool Render() override;
@ -132,7 +132,6 @@ public:
EmberNs::Renderer<T, float>* FirstOrDefaultRenderer();
protected:
void CancelPreviewRender();
void HandleFinishedProgress();
void SaveCurrentRender(Ember<T>& ember);
void SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc);
@ -143,10 +142,32 @@ protected:
void SetProgressComplete(int val);
Ember<T>* m_Ember;
Ember<T> m_PreviewEmber;
EmberFile<T> m_EmberFile;
EmberToXml<T> m_XmlWriter;
unique_ptr<EmberNs::Renderer<T, float>> m_FinalPreviewRenderer;
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_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage;
FinalRenderPreviewRenderer(FinalRenderEmberController<T>* controller) : m_Controller(controller)
{
}
virtual void PreviewRenderFunc(uint start, uint end) override;
private:
FinalRenderEmberController<T>* m_Controller;
};

View File

@ -198,6 +198,7 @@ Fractorium::Fractorium(QWidget* p)
/// </summary>
Fractorium::~Fractorium()
{
SyncSequenceSettings();
m_VarDialog->SyncSettings();
m_Settings->setValue("windowState", saveState());
m_Settings->sync();
@ -373,7 +374,7 @@ void Fractorium::closeEvent(QCloseEvent* e)
if (m_Controller.get())
{
m_Controller->StopRenderTimer(true);//Will wait until fully exited and stopped.
m_Controller->StopPreviewRender();
m_Controller->StopAllPreviewRenderers();
}
if (e)
@ -665,13 +666,15 @@ void Fractorium::ShowCritical(const QString& title, const QString& text, bool in
/// </summary>
void Fractorium::SetTabOrders()
{
QWidget* w = SetTabOrder(this, ui.ColorTable, m_BrightnessSpin);//Flame.
QWidget* w = SetTabOrder(this, ui.ColorTable, m_BrightnessSpin);//Flame color.
w = SetTabOrder(this, w, m_GammaSpin);
w = SetTabOrder(this, w, m_GammaThresholdSpin);
w = SetTabOrder(this, w, m_VibrancySpin);
w = SetTabOrder(this, w, m_HighlightSpin);
w = SetTabOrder(this, w, m_BackgroundColorButton);
w = SetTabOrder(this, w, m_PaletteModeCombo);
w = SetTabOrder(this, w, m_WidthSpin);//Flame geometry.
w = SetTabOrder(this, w, m_HeightSpin);
w = SetTabOrder(this, w, m_CenterXSpin);
w = SetTabOrder(this, w, m_CenterYSpin);
w = SetTabOrder(this, w, m_ScaleSpin);
@ -682,18 +685,44 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, m_PitchSpin);
w = SetTabOrder(this, w, m_YawSpin);
w = SetTabOrder(this, w, m_DepthBlurSpin);
w = SetTabOrder(this, w, m_SpatialFilterWidthSpin);
w = SetTabOrder(this, w, m_SpatialFilterWidthSpin);//Flame filter.
w = SetTabOrder(this, w, m_SpatialFilterTypeCombo);
w = SetTabOrder(this, w, m_TemporalFilterTypeCombo);
w = SetTabOrder(this, w, m_DEFilterMinRadiusSpin);
w = SetTabOrder(this, w, m_DEFilterMaxRadiusSpin);
w = SetTabOrder(this, w, m_DECurveSpin);
w = SetTabOrder(this, w, m_TemporalSamplesSpin);
w = SetTabOrder(this, w, m_SbsSpin);//Flame iteration.
w = SetTabOrder(this, w, m_FuseSpin);
w = SetTabOrder(this, w, m_QualitySpin);
w = SetTabOrder(this, w, m_SupersampleSpin);
w = SetTabOrder(this, w, m_InterpTypeCombo);//Flame animation.
w = SetTabOrder(this, w, m_AffineInterpTypeCombo);
w = SetTabOrder(this, w, m_InterpTypeCombo);
w = SetTabOrder(this, w, m_TemporalSamplesSpin);
w = SetTabOrder(this, w, m_TemporalFilterWidthSpin);
w = SetTabOrder(this, w, m_TemporalFilterTypeCombo);
w = SetTabOrder(this, ui.LibraryTree, ui.SequenceStartCountSpinBox);//Library.
w = SetTabOrder(this, w, ui.SequenceStartPreviewsButton);
w = SetTabOrder(this, w, ui.SequenceStopPreviewsButton);
w = SetTabOrder(this, w, ui.SequenceStartFlameSpinBox);
w = SetTabOrder(this, w, ui.SequenceStopFlameSpinBox);
w = SetTabOrder(this, w, ui.SequenceAllButton);
w = SetTabOrder(this, w, ui.SequenceRandomizeStaggerCheckBox);
w = SetTabOrder(this, w, ui.SequenceStaggerCheckBox);
w = SetTabOrder(this, w, ui.SequenceRandomizeFramesPerRotCheckBox);
w = SetTabOrder(this, w, ui.SequenceFramesPerRotSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomFramesPerRotMaxSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomizeRotationsCheckBox);
w = SetTabOrder(this, w, ui.SequenceRotationsSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomRotationsMaxSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomizeBlendFramesCheckBox);
w = SetTabOrder(this, w, ui.SequenceBlendFramesSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomBlendMaxFramesSpinBox);
w = SetTabOrder(this, w, ui.SequenceGenerateButton);
w = SetTabOrder(this, w, ui.SequenceRenderButton);
w = SetTabOrder(this, w, ui.SequenceSaveButton);
w = SetTabOrder(this, w, ui.SequenceOpenButton);
w = SetTabOrder(this, w, ui.SequenceTree);
w = SetTabOrder(this, ui.CurrentXformCombo, ui.AddXformButton);//Xforms.
w = SetTabOrder(this, w, ui.AddLinkedXformButton);
w = SetTabOrder(this, w, ui.DuplicateXformButton);
w = SetTabOrder(this, w, ui.ClearXformButton);
w = SetTabOrder(this, w, ui.DeleteXformButton);
@ -701,11 +730,14 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, m_XformWeightSpin);
w = SetTabOrder(this, w, m_XformWeightSpinnerButtonWidget->m_Button);
w = SetTabOrder(this, m_XformColorIndexSpin, ui.XformColorScroll);//Xforms color.
w = SetTabOrder(this, w, ui.RandomColorIndicesButton);
w = SetTabOrder(this, w, ui.ToggleColorIndicesButton);
w = SetTabOrder(this, w, m_XformColorSpeedSpin);
w = SetTabOrder(this, w, m_XformOpacitySpin);
w = SetTabOrder(this, w, m_XformDirectColorSpin);
w = SetTabOrder(this, w, ui.SoloXformCheckBox);
w = SetTabOrder(this, ui.PreAffineGroupBox, m_PreX1Spin);//Xforms affine.
w = SetTabOrder(this, ui.LockAffineCheckBox, ui.PreAffineGroupBox);//Xforms affine.
w = SetTabOrder(this, w, m_PreX1Spin);
w = SetTabOrder(this, w, m_PreX2Spin);
w = SetTabOrder(this, w, m_PreY1Spin);
w = SetTabOrder(this, w, m_PreY2Spin);
@ -727,6 +759,7 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, ui.PreScaleDownButton);
w = SetTabOrder(this, w, ui.PreScaleCombo);
w = SetTabOrder(this, w, ui.PreScaleUpButton);
w = SetTabOrder(this, w, ui.PreRandomButton);
w = SetTabOrder(this, w, ui.ShowPreAffineCurrentRadio);
w = SetTabOrder(this, w, ui.ShowPreAffineAllRadio);
w = SetTabOrder(this, w, ui.PostAffineGroupBox);
@ -752,21 +785,33 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, ui.PostScaleDownButton);
w = SetTabOrder(this, w, ui.PostScaleCombo);
w = SetTabOrder(this, w, ui.PostScaleUpButton);
w = SetTabOrder(this, w, ui.PostRandomButton);
w = SetTabOrder(this, w, ui.ShowPostAffineCurrentRadio);
w = SetTabOrder(this, w, ui.ShowPostAffineAllRadio);
w = SetTabOrder(this, w, ui.PolarAffineCheckBox);
w = SetTabOrder(this, w, ui.LocalPivotRadio);
w = SetTabOrder(this, w, ui.WorldPivotRadio);
w = SetTabOrder(this, ui.VariationsFilterLineEdit, ui.VariationsFilterClearButton);//Xforms variation.
w = SetTabOrder(this, w, ui.VariationsTree);
//Xforms xaos is done dynamically every time.
w = SetTabOrder(this, m_PaletteHueSpin, m_PaletteContrastSpin);//Palette.
w = SetTabOrder(this, ui.PaletteFilenameCombo, m_PaletteHueSpin);//Palette.
w = SetTabOrder(this, w, m_PaletteContrastSpin);
w = SetTabOrder(this, w, m_PaletteSaturationSpin);
w = SetTabOrder(this, w, m_PaletteBlurSpin);
w = SetTabOrder(this, w, m_PaletteBrightnessSpin);
w = SetTabOrder(this, w, m_PaletteFrequencySpin);
w = SetTabOrder(this, w, ui.PaletteRandomSelect);
w = SetTabOrder(this, w, ui.PaletteRandomAdjust);
w = SetTabOrder(this, w, ui.PaletteFilterLineEdit);
w = SetTabOrder(this, w, ui.PaletteFilterClearButton);
w = SetTabOrder(this, w, ui.PaletteListTable);
w = SetTabOrder(this, w, ui.ResetCurvesButton);//Palette curves.
w = SetTabOrder(this, w, ui.CurvesView);
w = SetTabOrder(this, w, ui.CurvesGroupBox);
w = SetTabOrder(this, w, ui.CurvesAllRadio);
w = SetTabOrder(this, w, ui.CurvesRedRadio);
w = SetTabOrder(this, w, ui.CurvesGreenRadio);
w = SetTabOrder(this, w, ui.CurvesBlueRadio);
w = SetTabOrder(this, ui.SummaryTable, ui.SummaryTree);//Info summary.
w = SetTabOrder(this, ui.InfoBoundsGroupBox, ui.InfoBoundsFrame);//Info bounds.
w = SetTabOrder(this, w, ui.InfoBoundsTable);

View File

@ -75,17 +75,24 @@ class Fractorium : public QMainWindow
friend FractoriumEmberController<float>;
friend FinalRenderEmberControllerBase;
friend FinalRenderEmberController<float>;
friend PreviewRenderer<float>;
friend TreePreviewRenderer<float>;
#ifdef DO_DOUBLE
friend GLEmberController<double>;
friend FractoriumEmberController<double>;
friend FinalRenderEmberController<double>;
friend PreviewRenderer<double>;
friend TreePreviewRenderer<double>;
#endif
public:
Fractorium(QWidget* p = nullptr);
~Fractorium();
//Library.
void SyncFileCountToSequenceCount();
//Geometry.
bool ApplyAll();
void SetCenter(float x, float y);
@ -156,6 +163,25 @@ public slots:
void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col);
void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col);
void OnDelete(const pair<size_t, QTreeWidgetItem*>& p);
void OnSequenceTreeItemChanged(QTreeWidgetItem* item, int col);
void OnSequenceStartPreviewsButtonClicked(bool checked);
void OnSequenceStopPreviewsButtonClicked(bool checked);
void OnSequenceAllButtonClicked(bool checked);
void OnSequenceGenerateButtonClicked(bool checked);
void OnSequenceRenderButtonClicked(bool checked);
void OnSequenceSaveButtonClicked(bool checked);
void OnSequenceOpenButtonClicked(bool checked);
void OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int state);
void OnSequenceRandomizeRotationsCheckBoxStateChanged(int state);
void OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int state);
void OnSequenceStartFlameSpinBoxChanged(int d);
void OnSequenceStopFlameSpinBoxChanged(int d);
void OnSequenceFramesPerRotSpinBoxChanged(int d);
void OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int d);
void OnSequenceRotationsSpinBoxChanged(double d);
void OnSequenceRandomRotationsMaxSpinBoxChanged(double d);
void OnSequenceBlendFramesSpinBoxChanged(int d);
void OnSequenceRandomBlendMaxFramesSpinBoxChanged(int d);
//Params.
void OnBrightnessChanged(double d);//Color.
@ -342,6 +368,7 @@ private:
//Library.
pair<size_t, QTreeWidgetItem*> GetCurrentEmberIndex();
void SyncSequenceSettings();
//Params.

View File

@ -74,7 +74,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1184</width>
<width>1016</width>
<height>987</height>
</rect>
</property>
@ -6683,7 +6683,7 @@
<property name="minimumSize">
<size>
<width>150</width>
<height>200</height>
<height>251</height>
</size>
</property>
<property name="maximumSize">
@ -6711,6 +6711,12 @@
<number>1</number>
</attribute>
<widget class="QWidget" name="DockWidgetContents">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
@ -6731,96 +6737,557 @@
<number>4</number>
</property>
<item>
<widget class="QScrollArea" name="LibraryTabScrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<widget class="QSplitter" name="LibrarySequenceSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="LibraryTabScrollWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>256</width>
<height>956</height>
</rect>
<widget class="LibraryTreeWidget" name="LibraryTree">
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
<layout class="QGridLayout" name="gridLayout_11">
<property name="leftMargin">
<number>0</number>
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::SelectedClicked</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="indentation">
<number>10</number>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="headerMinimumSectionSize">
<number>27</number>
</attribute>
<column>
<property name="text">
<string>Current Flame File</string>
</property>
<property name="topMargin">
<number>0</number>
</column>
</widget>
<widget class="QScrollArea" name="SequenceScrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="SequenceScrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>424</width>
<height>589</height>
</rect>
</property>
<property name="rightMargin">
<number>0</number>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="LibraryTreeWidget" name="LibraryTree">
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::SelectedClicked</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="indentation">
<number>10</number>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="headerMinimumSectionSize">
<number>27</number>
</attribute>
<column>
<property name="text">
<string>Current Flame File</string>
<layout class="QVBoxLayout" name="verticalLayout_10" stretch="3,0,8">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="SequenceParamsControlsGridLayout" columnstretch="0,0,0">
<property name="spacing">
<number>4</number>
</property>
</column>
</widget>
</item>
</layout>
<item row="4" column="1">
<widget class="QSpinBox" name="SequenceFramesPerRotSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Frames per rot: </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
<item row="3" column="1" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceStaggerCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Stagger</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="SequenceRotationsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Rotations: </string>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="value">
<double>3.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeFramesPerRotCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QSpinBox" name="SequenceRandomFramesPerRotMaxSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Frames per rot max: </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
<item row="3" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeStaggerCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Random</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="SequenceBlendFramesSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string/>
</property>
<property name="prefix">
<string>Blend frames: </string>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>120</number>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QSpinBox" name="SequenceRandomBlendMaxFramesSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Blend max frames: </string>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>120</number>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QDoubleSpinBox" name="SequenceRandomRotationsMaxSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Rotations max: </string>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="value">
<double>3.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeRotationsCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeBlendFramesCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="SequenceStartFlameSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>16777215</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Start flame: </string>
</property>
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="SequenceStopFlameSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Stop flame: </string>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="SequenceAllButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>33</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="SequenceStartCountSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Start count: </string>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="SequenceStartPreviewsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start Previews</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="SequenceStopPreviewsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Stop Previews</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="SequenceSaveButtonsHLayout" stretch="0,0,0,0">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QPushButton" name="SequenceGenerateButton">
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SequenceRenderButton">
<property name="text">
<string>Render</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SequenceSaveButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SequenceOpenButton">
<property name="text">
<string>Open</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="LibraryTreeWidget" name="SequenceTree">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::SelectedClicked</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="indentation">
<number>10</number>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="headerMinimumSectionSize">
<number>27</number>
</attribute>
<column>
<property name="text">
<string>Sequence</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>

View File

@ -406,3 +406,47 @@ static QList<T> GetAllParents(QWidget* widget)
return parents;
}
/// <summary>
/// Constrain the value in low to be less than or equal to the value in high.
/// Template expected to be any control which has member functions value() and setValue().
/// Most likely QSpinBox or QDoubleSpinbox.
/// </summary>
/// <param name="low">The control which must contain the lower value</param>
/// <param name="high">The control which must contain the higher value</param>
/// <returns>True if the value of low had to be changed, else false.</returns>
template <typename T>
bool ConstrainLow(T* low, T* high)
{
if (low->value() > high->value())
{
low->blockSignals(true);
low->setValue(high->value());
low->blockSignals(false);
return true;
}
return false;
}
/// <summary>
/// Constrain the value in high to be greater than or equal to the value in low.
/// Template expected to be any control which has member functions value() and setValue().
/// Most likely QSpinBox or QDoubleSpinbox.
/// </summary>
/// <param name="low">The control which must contain the lower value</param>
/// <param name="high">The control which must contain the higher value</param>
/// <returns>True if the value of high had to be changed, else false.</returns>
template <typename T>
bool ConstrainHigh(T* low, T* high)
{
if (high->value() < low->value())
{
high->blockSignals(true);
high->setValue(low->value());
high->blockSignals(false);
return true;
}
return false;
}

View File

@ -44,6 +44,8 @@ FractoriumEmberController<T>::FractoriumEmberController(Fractorium* fractorium)
{
bool b = false;
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);
//Initial combo change event to fill the palette table will be called automatically later.
//Look hard for a palette.
static vector<string> paths =
@ -70,57 +72,6 @@ FractoriumEmberController<T>::FractoriumEmberController(Fractorium* fractorium)
BackgroundChanged(QColor(0, 0, 0));//Default to black.
ClearUndo();
m_PreviewRenderer->Callback(nullptr);
m_PreviewRenderer->NumChannels(4);
m_PreviewRenderer->EarlyClip(m_Fractorium->m_Settings->EarlyClip());
m_PreviewRenderer->YAxisUp(m_Fractorium->m_Settings->YAxisUp());
m_PreviewRenderer->SetEmber(m_Ember);//Give it an initial ember, will be updated many times later.
//m_PreviewRenderer->ThreadCount(1);//For debugging.
m_PreviewRenderFunc = [&](uint start, uint end)
{
while (m_PreviewRun || m_PreviewRunning)
{
}
m_PreviewRun = true;
m_PreviewRunning = true;
m_PreviewRenderer->ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
auto tree = m_Fractorium->ui.LibraryTree;
if (auto top = tree->topLevelItem(0))
{
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)
{
Ember<T> ember = *b;
ember.SyncSize();
ember.SetSizeAndAdjustScale(PREVIEW_SIZE, PREVIEW_SIZE, false, eScaleType::SCALE_WIDTH);
ember.m_TemporalSamples = 1;
ember.m_Quality = 25;
ember.m_Supersample = 1;
m_PreviewRenderer->SetEmber(ember);
if (m_PreviewRenderer->Run(m_PreviewFinalImage) == eRenderStatus::RENDER_OK)
{
if (auto treeItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(int(i))))
{
//It is critical that Qt::BlockingQueuedConnection is passed because this is running on a different thread than the UI.
//This ensures the events are processed in order as each preview is updated, and that control does not return here
//until the update is complete.
QMetaObject::invokeMethod(m_Fractorium, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection,
Q_ARG(EmberTreeWidgetItemBase*, dynamic_cast<EmberTreeWidgetItemBase*>(treeItem)),
Q_ARG(vector<byte>&, m_PreviewFinalImage),
Q_ARG(uint, PREVIEW_SIZE),
Q_ARG(uint, PREVIEW_SIZE));
}
}
}
}
m_PreviewRun = false;
m_PreviewRunning = false;
};
}
/// <summary>
@ -136,11 +87,19 @@ FractoriumEmberController<T>::~FractoriumEmberController() { }
/// </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) { m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation)
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)
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, 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; }
@ -148,11 +107,19 @@ template <typename T> void FractoriumEmberController<T>::CopyTempPalette(Palette
#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) { m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation)
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)
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, 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; }
@ -387,8 +354,58 @@ void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool v
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)
{
auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.Transparency(f->m_Settings->Transparency());
m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
if (auto top = m_Tree->topLevelItem(0))
{
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);
if (m_PreviewRenderer.Run(m_PreviewFinalImage) == eRenderStatus::RENDER_OK)
{
if (auto treeItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(int(i))))
{
//It is critical that Qt::BlockingQueuedConnection is passed because this is running on a different thread than the UI.
//This ensures the events are processed in order as each preview is updated, and that control does not return here
//until the update is complete.
QMetaObject::invokeMethod(f, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection,
Q_ARG(EmberTreeWidgetItemBase*, treeItem),
Q_ARG(vector<byte>&, m_PreviewFinalImage),
Q_ARG(uint, PREVIEW_SIZE),
Q_ARG(uint, PREVIEW_SIZE));
}
}
}
}
}
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

View File

@ -29,6 +29,9 @@ enum eLibraryUpdate { INDEX = 1, NAME = 2, POINTER = 4 };
/// So Fractorium includes this file, and Fractorium is declared as a forward declaration here.
/// </summary>
class Fractorium;
template <typename T> class PreviewRenderer;
template <typename T> class TreePreviewRenderer;
#define PREVIEW_SIZE 256
#define UNDO_SIZE 128
@ -54,20 +57,19 @@ public:
//Embers.
virtual void SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) { }
virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }//Uncomment default lambdas once LLVM fixes a crash in their compiler with default lambda parameters.//TODO
virtual void SetEmberFile(const EmberFile<float>& emberFile) { }
virtual void CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }
virtual void SetEmberFile(const EmberFile<float>& emberFile, bool move) { }
virtual void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }
virtual void SetTempPalette(const Palette<float>& palette) { }
virtual void CopyTempPalette(Palette<float>& palette) { }
#ifdef DO_DOUBLE
virtual void SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) { }
virtual void CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) { }
virtual void SetEmberFile(const EmberFile<double>& emberFile) { }
virtual void CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) { }
virtual void SetEmberFile(const EmberFile<double>& emberFile, bool move) { }
virtual void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) { }
virtual void SetTempPalette(const Palette<double>& palette) { }
virtual void CopyTempPalette(Palette<double>& palette) { }
#endif
virtual void SetEmber(size_t index, bool verbatim) { }
//virtual void Clear() { }
virtual void AddXform() { }
virtual void AddLinkedXform() { }
virtual void DuplicateXform() { }
@ -119,10 +121,18 @@ public:
virtual void UpdateLibraryTree() { }
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) { }
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { }
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { }
virtual void StopPreviewRender() { }
virtual void RenderLibraryPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { }
virtual void RenderSequencePreviews(uint start = UINT_MAX, uint end = UINT_MAX) { }
virtual void SequenceTreeItemChanged(QTreeWidgetItem* item, int col) { }
virtual void StopLibraryPreviewRender() { }
virtual void StopSequencePreviewRender() { }
virtual void StopAllPreviewRenderers() { }
virtual void MoveLibraryItems(int startRow, int destRow) { }
virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) { }
virtual void FillSequenceTree() { }
virtual void SequenceGenerateButtonClicked() { }
virtual void SequenceSaveButtonClicked() { }
virtual void SequenceOpenButtonClicked() { }
//Params.
virtual void SetCenter(double x, double y) { }
@ -287,6 +297,9 @@ protected:
template<typename T>
class FractoriumEmberController : public FractoriumEmberControllerBase
{
friend PreviewRenderer<T>;
friend TreePreviewRenderer<T>;
public:
FractoriumEmberController(Fractorium* fractorium);
FractoriumEmberController(const FractoriumEmberController<T>& controller) = delete;
@ -295,20 +308,19 @@ public:
//Embers.
virtual void SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) override;
virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<float>& emberFile) override;
virtual void CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<float>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
virtual void SetTempPalette(const Palette<float>& palette) override;
virtual void CopyTempPalette(Palette<float>& palette) override;
#ifdef DO_DOUBLE
virtual void SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) override;
virtual void CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<double>& emberFile) override;
virtual void CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<double>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
virtual void SetTempPalette(const Palette<double>& palette) override;
virtual void CopyTempPalette(Palette<double>& palette) override;
#endif
virtual void SetEmber(size_t index, bool verbatim) override;
//virtual void Clear() override { }
virtual void AddXform() override;
virtual void AddLinkedXform() override;
virtual void DuplicateXform() override;
@ -316,7 +328,6 @@ public:
virtual void DeleteXforms() override;
virtual void AddFinalXform() override;
virtual bool UseFinalXform() override { return m_Ember.UseFinalXform(); }
//virtual bool IsFinal(uint i) { return false; }
virtual size_t XformCount() const override { return m_Ember.XformCount(); }
virtual size_t TotalXformCount() const override { return m_Ember.TotalXformCount(); }
virtual QString Name() const override { return QString::fromStdString(m_Ember.m_Name); }
@ -365,8 +376,17 @@ public:
virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) override;
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) override;
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) override;
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override;
virtual void StopPreviewRender() override;
void RenderPreviews(QTreeWidget* tree, TreePreviewRenderer<T>* renderer, EmberFile<T>& file, uint start = UINT_MAX, uint end = UINT_MAX);
virtual void RenderLibraryPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override;
virtual void RenderSequencePreviews(uint start = UINT_MAX, uint end = UINT_MAX) override;
virtual void SequenceTreeItemChanged(QTreeWidgetItem* item, int col) override;
virtual void StopLibraryPreviewRender() override;
virtual void StopSequencePreviewRender() override;
virtual void StopAllPreviewRenderers() override;
virtual void FillSequenceTree() override;
virtual void SequenceGenerateButtonClicked() override;
virtual void SequenceSaveButtonClicked() override;
virtual void SequenceOpenButtonClicked() override;
//Params.
virtual void SetCenter(double x, double y) override;
@ -504,13 +524,13 @@ private:
bool SyncSizes();
//Templated members.
bool m_PreviewRun = false;
bool m_PreviewRunning = false;
vector<T> m_TempOpacities;
vector<T> m_NormalizedWeights;
Ember<T> m_Ember;
const void* m_EmberFilePointer = nullptr;
EmberFile<T> m_EmberFile;
EmberFile<T> m_SequenceFile;
deque<Ember<T>> m_UndoList;
vector<Xform<T>> m_CopiedXforms;
Xform<T> m_CopiedFinalXform;
@ -519,8 +539,115 @@ private:
shared_ptr<VariationList<T>> m_VariationList;
unique_ptr<SheepTools<T, float>> m_SheepTools;
unique_ptr<GLEmberController<T>> m_GLController;
unique_ptr<EmberNs::Renderer<T, float>> m_PreviewRenderer = make_unique<EmberNs::Renderer<T, float>>();
QFuture<void> m_PreviewResult;
std::function<void (uint, uint)> m_PreviewRenderFunc;
unique_ptr<TreePreviewRenderer<T>> m_LibraryPreviewRenderer;
unique_ptr<TreePreviewRenderer<T>> m_SequencePreviewRenderer;
};
/// <summary>
/// Base class for encapsulating a preview renderer which will be used
/// in such places as the main library tree, the sequence tree and the
/// single preview thumbnail shown in the final render dialog.
/// Derived classes will implement PreviewRenderFunc() to handle the rendering
/// functionality specific to their previews.
/// </summary>
template <typename T>
class PreviewRenderer
{
public:
PreviewRenderer()
{
}
void Render(uint start, uint end)
{
Stop();
m_PreviewResult = QtConcurrent::run([&](uint s, uint e)
{
rlg l(m_PreviewCs);
m_PreviewRun = true;
PreviewRenderFunc(s, e);
m_PreviewRun = false;
}, start, end);
}
void Stop()
{
m_PreviewRun = false;
m_PreviewRenderer.Abort();
m_PreviewResult.cancel();
while (m_PreviewResult.isRunning())
QApplication::processEvents();
}
bool EarlyClip()
{
return m_PreviewRenderer.EarlyClip();
}
bool YAxisUp()
{
return m_PreviewRenderer.YAxisUp();
}
bool Transparency()
{
return m_PreviewRenderer.Transparency();
}
bool Running()
{
return m_PreviewRun || m_PreviewResult.isRunning();
}
virtual void PreviewRenderFunc(uint start, uint end) {}
protected:
volatile bool m_PreviewRun = false;
Ember<T> m_PreviewEmber;
vector<byte> m_PreviewFinalImage;
EmberNs::Renderer<T, float> m_PreviewRenderer;
private:
QFuture<void> m_PreviewResult;
std::recursive_mutex m_PreviewCs;
};
/// <summary>
/// Thin derivation to handle preview rendering multiple embers previews to a tree.
/// </summary>
template <typename T>
class TreePreviewRenderer : public PreviewRenderer<T>
{
public:
using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage;
/// <summary>
/// Initializes a new instance of the <see cref="TreePreviewRenderer{T}"/> class.
/// </summary>
/// <param name="controller">A pointer to the controller this instance is a member of</param>
/// <param name="tree">A pointer to the tree to render to</param>
/// <param name="emberFile">A reference to the ember file to render</param>
TreePreviewRenderer(FractoriumEmberController<T>* controller, QTreeWidget* tree, EmberFile<T>& emberFile) :
m_Controller(controller),
m_Tree(tree),
m_EmberFile(emberFile)
{
auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.Callback(nullptr);
m_PreviewRenderer.NumChannels(4);
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.Transparency(f->m_Settings->Transparency());
}
virtual void PreviewRenderFunc(uint start, uint end) override;
protected:
FractoriumEmberController<T>* m_Controller;
QTreeWidget* m_Tree;
EmberFile<T>& m_EmberFile;
};

View File

@ -7,9 +7,34 @@
void Fractorium::InitLibraryUI()
{
ui.LibraryTree->SetMainWindow(this);
connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
ui.SequenceFramesPerRotSpinBox->setValue(m_Settings->FramesPerRot());
ui.SequenceRandomFramesPerRotMaxSpinBox->setValue(m_Settings->FramesPerRotMax());
ui.SequenceRotationsSpinBox->setValue(m_Settings->Rotations());
ui.SequenceRandomRotationsMaxSpinBox->setValue(m_Settings->RotationsMax());
ui.SequenceBlendFramesSpinBox->setValue(m_Settings->BlendFrames());
ui.SequenceRandomBlendMaxFramesSpinBox->setValue(m_Settings->BlendFramesMax());
connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.SequenceTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnSequenceTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.SequenceStartPreviewsButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceStartPreviewsButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceStopPreviewsButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceStopPreviewsButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceAllButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceAllButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceGenerateButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceGenerateButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceRenderButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceRenderButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceSaveButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceSaveButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceOpenButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceOpenButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceRandomizeFramesPerRotCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomizeRotationsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSequenceRandomizeRotationsCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomizeBlendFramesCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceStartFlameSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceStartFlameSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceStopFlameSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceStopFlameSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceFramesPerRotSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceFramesPerRotSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomFramesPerRotMaxSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRotationsSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSequenceRotationsSpinBoxChanged(double)), Qt::QueuedConnection);
connect(ui.SequenceRandomRotationsMaxSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSequenceRandomRotationsMaxSpinBoxChanged(double)), Qt::QueuedConnection);
connect(ui.SequenceBlendFramesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceBlendFramesSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomBlendMaxFramesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomBlendMaxFramesSpinBoxChanged(int)), Qt::QueuedConnection);
}
/// <summary>
@ -95,7 +120,7 @@ void FractoriumEmberController<T>::FillLibraryTree(int selectIndex)
uint i = 0;
auto tree = m_Fractorium->ui.LibraryTree;
vector<byte> v(size * size * 4);
StopPreviewRender();
StopAllPreviewRenderers();
tree->clear();
QCoreApplication::flush();
tree->blockSignals(true);
@ -125,8 +150,9 @@ void FractoriumEmberController<T>::FillLibraryTree(int selectIndex)
if (auto emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(selectIndex)))
emberItem->setSelected(true);
m_Fractorium->SyncFileCountToSequenceCount();
QCoreApplication::flush();
RenderPreviews(0, uint(m_EmberFile.Size()));
RenderLibraryPreviews(0, uint(m_EmberFile.Size()));
tree->expandAll();
}
@ -162,8 +188,9 @@ void FractoriumEmberController<T>::UpdateLibraryTree()
//When adding elements, ensure all indices are sequential.
SyncLibrary(eLibraryUpdate::INDEX);
m_Fractorium->SyncFileCountToSequenceCount();
tree->blockSignals(false);
RenderPreviews(origChildCount, uint(m_EmberFile.Size()));
RenderLibraryPreviews(origChildCount, uint(m_EmberFile.Size()));
}
}
@ -280,6 +307,7 @@ void FractoriumEmberController<T>::Delete(const pair<size_t, QTreeWidgetItem*>&
{
delete p.second;
SyncLibrary(eLibraryUpdate::INDEX);
m_Fractorium->SyncFileCountToSequenceCount();
}
//If there is now only one item left and it wasn't selected, select it.
@ -308,13 +336,12 @@ void Fractorium::OnDelete(const pair<size_t, QTreeWidgetItem*>& p)
/// <param name="start">The 0-based index to start rendering previews for</param>
/// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param>
template <typename T>
void FractoriumEmberController<T>::RenderPreviews(uint start, uint end)
void FractoriumEmberController<T>::RenderPreviews(QTreeWidget* tree, TreePreviewRenderer<T>* renderer, EmberFile<T>& file, uint start, uint end)
{
StopPreviewRender();
renderer->Stop();
if (start == UINT_MAX && end == UINT_MAX)
{
auto tree = m_Fractorium->ui.LibraryTree;
tree->blockSignals(true);
if (auto top = tree->topLevelItem(0))
@ -323,36 +350,401 @@ void FractoriumEmberController<T>::RenderPreviews(uint start, uint end)
vector<byte> emptyPreview(PREVIEW_SIZE * PREVIEW_SIZE * 4);
for (int i = 0; i < childCount; i++)
if (auto treeItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
if (auto treeItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(i)))
treeItem->SetImage(emptyPreview, PREVIEW_SIZE, PREVIEW_SIZE);
}
tree->blockSignals(false);
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, 0, uint(m_EmberFile.Size()));
renderer->Render(0, uint(file.Size()));
}
else
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, start, end);
renderer->Render(start, end);
}
/// <summary>
/// Stop the preview rendering thread.
/// Wrapper around calling RenderPreviews with the appropriate values passed in for the previews in the main library 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 FractoriumEmberController<T>::RenderLibraryPreviews(uint start, uint end)
{
RenderPreviews(m_Fractorium->ui.LibraryTree, m_LibraryPreviewRenderer.get(), m_EmberFile, start, end);
}
template <typename T>
void FractoriumEmberController<T>::StopLibraryPreviewRender() { m_LibraryPreviewRenderer->Stop(); }
/// <summary>
/// Thing wrapper around StopLibraryPreviewRender() and StopSequencePreviewRender() to stop both preview renderers.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::StopPreviewRender()
void FractoriumEmberController<T>::StopAllPreviewRenderers()
{
m_PreviewRun = false;
m_PreviewRenderer->Abort();
StopLibraryPreviewRender();
StopSequencePreviewRender();
}
while (m_PreviewRunning)
QApplication::processEvents();
m_PreviewResult.cancel();
while (m_PreviewResult.isRunning())
QApplication::processEvents();
QCoreApplication::sendPostedEvents(m_Fractorium->ui.LibraryTree);
/// <summary>
/// Fill the sequence tree with the names of the embers in the
/// currently generated sequence.
/// Start the sequence preview render thread.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillSequenceTree()
{
uint size = 64;
uint i = 0;
auto tree = m_Fractorium->ui.SequenceTree;
vector<byte> v(size * size * 4);
m_SequencePreviewRenderer->Stop();
tree->clear();
QCoreApplication::flush();
tree->blockSignals(true);
auto fileItem = new QTreeWidgetItem(tree);
QFileInfo info(m_SequenceFile.m_Filename);
fileItem->setText(0, info.fileName());
fileItem->setToolTip(0, m_SequenceFile.m_Filename);
fileItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
for (auto& it : m_SequenceFile.m_Embers)
{
auto emberItem = new EmberTreeWidgetItemBase(fileItem);
if (it.m_Name.empty())
emberItem->setText(0, ToString(i++));
else
emberItem->setText(0, it.m_Name.c_str());
emberItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
emberItem->setToolTip(0, emberItem->text(0));
emberItem->SetImage(v, size, size);
}
tree->blockSignals(false);
QCoreApplication::flush();
RenderSequencePreviews(0, uint(m_SequenceFile.Size()));
tree->expandAll();
}
/// <summary>
/// Copy the text of the root item to the name of the sequence file.
/// Called whenever the text of the root item is changed.
/// </summary>
/// <param name="item">The root sequence tree item which changed</param>
/// <param name="col">The column clicked, ignored.</param>
template <typename T>
void FractoriumEmberController<T>::SequenceTreeItemChanged(QTreeWidgetItem* item, int col)
{
if (auto parentItem = dynamic_cast<QTreeWidgetItem*>(item))
{
QString text = parentItem->text(0);
if (text != "")
m_SequenceFile.m_Filename = text;
}
}
void Fractorium::OnSequenceTreeItemChanged(QTreeWidgetItem* item, int col) { m_Controller->SequenceTreeItemChanged(item, col); }
/// <summary>
/// Wrapper around calling RenderPreviews with the appropriate values passed in for the previews in the sequence tree.
/// Called when Render Previews is clicked.
/// </summary>
/// <param name="start">Ignored, render all.</param>
/// <param name="end">Ignored, render all.</param>
template <typename T>
void FractoriumEmberController<T>::RenderSequencePreviews(uint start, uint end) { RenderPreviews(m_Fractorium->ui.SequenceTree, m_SequencePreviewRenderer.get(), m_SequenceFile, start, end); }
void Fractorium::OnSequenceStartPreviewsButtonClicked(bool checked) { m_Controller->RenderSequencePreviews(); }
/// <summary>
/// Stop rendering the sequence previews.
/// Called when Stop Previews is clicked.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::StopSequencePreviewRender() { m_SequencePreviewRenderer->Stop(); }
void Fractorium::OnSequenceStopPreviewsButtonClicked(bool checked) { m_Controller->StopSequencePreviewRender(); }
/// <summary>
/// Set the start and stop spin boxes to 0 and the length of the ember file minus 1, respectively.
/// Called whenever the count of the current file changes or when All is clicked.
/// </summary>
void Fractorium::SyncFileCountToSequenceCount()
{
if (auto top = ui.LibraryTree->topLevelItem(0))
{
int count = top->childCount() - 1;
ui.SequenceStartFlameSpinBox->setMinimum(0);
ui.SequenceStartFlameSpinBox->setMaximum(count);
ui.SequenceStartFlameSpinBox->setValue(0);
ui.SequenceStopFlameSpinBox->setMinimum(0);
ui.SequenceStopFlameSpinBox->setMaximum(count);
ui.SequenceStopFlameSpinBox->setValue(count);
}
}
void Fractorium::OnSequenceAllButtonClicked(bool checked) { SyncFileCountToSequenceCount(); }
/// <summary>
/// Generate an animation sequence and place it in the sequence tree. This code is
/// mostly similar to that of EmberGenome.
/// It differs in a few ways:
/// The number of frames used in a rotation and in blending can differ. In EmberGenome, they are the same.
/// The number of rotations, frames used in rotations and frames used in blending can all be randomized.
/// Called when the Generate button is clicked.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::SequenceGenerateButtonClicked()
{
StopAllPreviewRenderers();
SaveCurrentToOpenedFile(false);
Ember<T> result;
auto& ui = m_Fractorium->ui;
auto s = m_Fractorium->m_Settings;
bool randStagger = ui.SequenceRandomizeStaggerCheckBox->isChecked();
bool randFramesRot = ui.SequenceRandomizeFramesPerRotCheckBox->isChecked();
bool randRot = ui.SequenceRandomizeRotationsCheckBox->isChecked();
bool randBlend = ui.SequenceRandomizeBlendFramesCheckBox->isChecked();
bool stagger = ui.SequenceStaggerCheckBox->isChecked();
double rots = ui.SequenceRotationsSpinBox->value();
double rotsMax = ui.SequenceRandomRotationsMaxSpinBox->value();
int framesPerRot = ui.SequenceFramesPerRotSpinBox->value();
int framesPerRotMax = ui.SequenceRandomFramesPerRotMaxSpinBox->value();
int framesBlend = ui.SequenceBlendFramesSpinBox->value();
int framesBlendMax = ui.SequenceRandomBlendMaxFramesSpinBox->value();
size_t start = ui.SequenceStartFlameSpinBox->value();
size_t stop = ui.SequenceStopFlameSpinBox->value();
size_t startCount = ui.SequenceStartCountSpinBox->value();
size_t keyFrames = (stop - start) + 1;
size_t frameCount = 0;
double frames = 0;
vector<pair<size_t, size_t>> devices;//Dummy.
EmberReport emberReport;
ostringstream os;
string palettePath =
#ifdef _WIN32
"./flam3-palettes.xml";
#else
"~/.config/fractorium";
#endif
SheepTools<T, float> tools(palettePath, EmberCommon::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, false, 0, emberReport));
tools.SetSpinParams(true,
randStagger ? m_Rand.RandBit() : stagger,
0,
0,
s->Nick().toStdString(),
s->Url().toStdString(),
s->Id().toStdString(),
"",
0,
0);
if (randFramesRot)
frames = ui.SequenceRandomFramesPerRotMaxSpinBox->value();
else
frames = ui.SequenceFramesPerRotSpinBox->value();
if (randRot)
frames *= ui.SequenceRandomRotationsMaxSpinBox->value();
else
frames *= ui.SequenceRotationsSpinBox->value();
if (randBlend)
frames += ui.SequenceRandomBlendMaxFramesSpinBox->value();
else
frames += ui.SequenceBlendFramesSpinBox->value();
frames *= keyFrames;
frames += startCount;
os << setfill('0') << setprecision(0) << fixed;
m_SequenceFile.Clear();
m_SequenceFile.m_Filename = EmberFile<T>::DefaultFilename("Sequence_");
double blend;
size_t frame;
Ember<T> embers[2];//Spin needs contiguous array below, and this will also get modified, so a copy is needed to avoid modifying the embers in the original file.
auto padding = streamsize(std::log10(frames)) + 1;
auto it = Advance(m_EmberFile.m_Embers.begin(), start);
for (size_t i = start; i <= stop && it != m_EmberFile.m_Embers.end(); i++, ++it)
{
double rotations = randRot ? m_Rand.Frand<double>(rots, rotsMax) : rots;
embers[0] = *it;
if (rotations > 0)
{
double rotFrames = randFramesRot ? m_Rand.Frand<double>(framesPerRot, framesPerRotMax) : framesPerRot;
size_t roundFrames = size_t(std::round(rotFrames * rotations));
for (frame = 0; frame < roundFrames; frame++)
{
blend = frame / rotFrames;
tools.Spin(embers[0], nullptr, result, startCount + frameCount++, blend);//Result is cleared and reassigned each time inside of Spin().
FormatName(result, os, padding);
m_SequenceFile.m_Embers.push_back(result);
}
//The loop above will have rotated just shy of a complete rotation.
//Rotate the next step and save in result, but do not print.
//result will be the starting point for the interp phase below.
frame = roundFrames;
blend = frame / rotFrames;
tools.Spin(embers[0], nullptr, result, startCount + frameCount, blend);//Do not increment frameCount here.
FormatName(result, os, padding);
}
if (i < stop)
{
if (rotations > 0)//Store the last result as the flame to interpolate from. This applies for whole or fractional values of opt.Loops().
embers[0] = result;
auto it2 = it;//Need a quick temporary to avoid modifying it which is used in the loop.
embers[1] = *(++it2);//Get the next ember to be used with blending below.
size_t blendFrames = randBlend ? m_Rand.Frand<double>(framesBlend, framesBlendMax) : framesBlend;
for (frame = 0; frame < blendFrames; frame++)
{
bool seqFlag = frame == 0 || (frame == blendFrames - 1);
blend = frame / double(blendFrames);
result.Clear();
if (randStagger)
tools.Stagger(m_Rand.RandBit());
tools.SpinInter(&embers[0], nullptr, result, startCount + frameCount++, seqFlag, blend);
FormatName(result, os, padding);
m_SequenceFile.m_Embers.push_back(result);
}
}
}
it = Advance(m_EmberFile.m_Embers.begin(), stop);
tools.Spin(*it, nullptr, result, startCount + frameCount, 0);
FormatName(result, os, padding);
m_SequenceFile.m_Embers.push_back(result);
FillSequenceTree();//The sequence has been generated, now create preview thumbnails.
}
void Fractorium::OnSequenceGenerateButtonClicked(bool checked) { m_Controller->SequenceGenerateButtonClicked(); }
/// <summary>
/// Show the final render dialog and load the sequence into it.
/// This will automatically check the Render All and Render as Animation sequence checkboxes.
/// Called when the Render Sequence button is clicked.
/// </summary>
/// <param name="checked">Ignored.</param>
void Fractorium::OnSequenceRenderButtonClicked(bool checked)
{
//First completely stop what the current rendering process is doing.
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
m_Controller->StopAllPreviewRenderers();
m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file.
m_RenderStatusLabel->setText("Renderer stopped.");
m_FinalRenderDialog->Show(true);//Show with a bool specifying that it came from the sequence generator.
}
/// <summary>
/// Save the sequence to a file.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::SequenceSaveButtonClicked()
{
auto s = m_Fractorium->m_Settings;
QString filename = m_Fractorium->SetupSaveXmlDialog(m_SequenceFile.m_Filename);
if (filename != "")
{
EmberToXml<T> writer;
QFileInfo fileInfo(filename);
if (writer.Save(filename.toStdString().c_str(), m_SequenceFile.m_Embers, 0, true, true))
s->SaveFolder(fileInfo.canonicalPath());
else
m_Fractorium->ShowCritical("Save Failed", "Could not save sequence file, try saving to a different folder.");
}
}
void Fractorium::OnSequenceSaveButtonClicked(bool checked) { m_Controller->SequenceSaveButtonClicked(); }
/// <summary>
/// Open one or more sequence file, concatenate them all, place them in the sequence
/// tree and begin rendering the sequence previews.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::SequenceOpenButtonClicked()
{
m_SequencePreviewRenderer->Stop();
auto filenames = m_Fractorium->SetupOpenXmlDialog();
if (!filenames.empty())
{
size_t i;
EmberFile<T> emberFile;
XmlToEmber<T> parser;
vector<Ember<T>> embers;
vector<string> errors;
emberFile.m_Filename = filenames[0];
for (auto& filename : filenames)
{
embers.clear();
if (parser.Parse(filename.toStdString().c_str(), embers) && !embers.empty())
{
for (i = 0; i < embers.size(); i++)
if (embers[i].m_Name == "" || embers[i].m_Name == "No name")//Ensure it has a name.
embers[i].m_Name = ToString<qulonglong>(i).toStdString();
emberFile.m_Embers.insert(emberFile.m_Embers.end(), embers.begin(), embers.end());
errors = parser.ErrorReport();
}
else
{
errors = parser.ErrorReport();
m_Fractorium->ShowCritical("Open Failed", "Could not open sequence file, see info tab for details.");
}
if (!errors.empty())
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit, false);//Concat errors from all files.
}
if (emberFile.Size() > 0)//Ensure at least something was read.
{
emberFile.MakeNamesUnique();
m_SequenceFile = std::move(emberFile);//Move the temp to avoid creating dupes because we no longer need it.
FillSequenceTree();
}
}
}
void Fractorium::OnSequenceOpenButtonClicked(bool checked) { m_Controller->SequenceOpenButtonClicked(); }
void Fractorium::OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int state) { ui.SequenceRandomFramesPerRotMaxSpinBox->setEnabled(state); }
void Fractorium::OnSequenceRandomizeRotationsCheckBoxStateChanged(int state) { ui.SequenceRandomRotationsMaxSpinBox->setEnabled(state); }
void Fractorium::OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int state) { ui.SequenceRandomBlendMaxFramesSpinBox->setEnabled(state); }
/// <summary>
/// Constrain all min/max spinboxes.
/// </summary>
void Fractorium::OnSequenceStartFlameSpinBoxChanged(int d) { ConstrainLow(ui.SequenceStartFlameSpinBox, ui.SequenceStopFlameSpinBox); }
void Fractorium::OnSequenceStopFlameSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceStartFlameSpinBox, ui.SequenceStopFlameSpinBox); }
void Fractorium::OnSequenceFramesPerRotSpinBoxChanged(int d) { if (ui.SequenceRandomizeFramesPerRotCheckBox->isChecked()) ConstrainLow(ui.SequenceFramesPerRotSpinBox, ui.SequenceRandomFramesPerRotMaxSpinBox); }
void Fractorium::OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceFramesPerRotSpinBox, ui.SequenceRandomFramesPerRotMaxSpinBox); }
void Fractorium::OnSequenceRotationsSpinBoxChanged(double d) { if (ui.SequenceRandomizeRotationsCheckBox->isChecked()) ConstrainLow(ui.SequenceRotationsSpinBox, ui.SequenceRandomRotationsMaxSpinBox); }
void Fractorium::OnSequenceRandomRotationsMaxSpinBoxChanged(double d) { ConstrainHigh(ui.SequenceRotationsSpinBox, ui.SequenceRandomRotationsMaxSpinBox); }
void Fractorium::OnSequenceBlendFramesSpinBoxChanged(int d) { if (ui.SequenceRandomizeBlendFramesCheckBox->isChecked()) ConstrainLow(ui.SequenceBlendFramesSpinBox, ui.SequenceRandomBlendMaxFramesSpinBox); }
void Fractorium::OnSequenceRandomBlendMaxFramesSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceBlendFramesSpinBox, ui.SequenceRandomBlendMaxFramesSpinBox); }
/// <summary>
/// Save all sequence settings to match the values in the controls.
/// </summary>
void Fractorium::SyncSequenceSettings()
{
m_Settings->FramesPerRot(ui.SequenceFramesPerRotSpinBox->value());
m_Settings->FramesPerRotMax(ui.SequenceRandomFramesPerRotMaxSpinBox->value());
m_Settings->Rotations(ui.SequenceRotationsSpinBox->value());
m_Settings->RotationsMax(ui.SequenceRandomRotationsMaxSpinBox->value());
m_Settings->BlendFrames(ui.SequenceBlendFramesSpinBox->value());
m_Settings->BlendFramesMax(ui.SequenceRandomBlendMaxFramesSpinBox->value());
}
template class FractoriumEmberController<float>;

View File

@ -52,7 +52,7 @@ template <typename T>
void FractoriumEmberController<T>::NewFlock(size_t count)
{
Ember<T> ember;
StopPreviewRender();
StopAllPreviewRenderers();
m_EmberFile.Clear();
m_EmberFile.m_Filename = EmberFile<T>::DefaultFilename();
@ -91,7 +91,7 @@ void FractoriumEmberController<T>::NewEmptyFlameInCurrentFile()
Ember<T> ember;
Xform<T> xform;
QDateTime local(QDateTime::currentDateTime());
StopPreviewRender();
StopAllPreviewRenderers();
ParamsToEmber(ember);
xform.m_Weight = T(0.25);
xform.m_ColorX = m_Rand.Frand01<T>();
@ -116,7 +116,7 @@ template <typename T>
void FractoriumEmberController<T>::NewRandomFlameInCurrentFile()
{
Ember<T> ember;
StopPreviewRender();
StopAllPreviewRenderers();
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<int>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedFrand<T>(-2, 2)), 0, MAX_CL_VARS);
ParamsToEmber(ember);
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
@ -138,7 +138,7 @@ template <typename T>
void FractoriumEmberController<T>::CopyFlameInCurrentFile()
{
auto ember = m_Ember;
StopPreviewRender();
StopAllPreviewRenderers();
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
ember.m_Index = m_EmberFile.Size();
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
@ -172,7 +172,7 @@ void FractoriumEmberController<T>::OpenAndPrepFiles(const QStringList& filenames
vector<Ember<T>> embers;
vector<string> errors;
uint previousSize = append ? uint(m_EmberFile.Size()) : 0u;
StopPreviewRender();
StopAllPreviewRenderers();
emberFile.m_Filename = filenames[0];
for (auto& filename : filenames)
@ -377,7 +377,7 @@ uint FractoriumEmberController<T>::SaveCurrentToOpenedFile(bool render)
uint i = 0;
bool fileFound = false;
if (!m_PreviewRunning)
if (!m_LibraryPreviewRenderer->Running())
{
for (auto& it : m_EmberFile.m_Embers)
{
@ -393,7 +393,7 @@ uint FractoriumEmberController<T>::SaveCurrentToOpenedFile(bool render)
if (!fileFound)
{
StopPreviewRender();
StopAllPreviewRenderers();
m_EmberFile.m_Embers.push_back(m_Ember);
m_EmberFile.MakeNamesUnique();
@ -401,7 +401,7 @@ uint FractoriumEmberController<T>::SaveCurrentToOpenedFile(bool render)
UpdateLibraryTree();
}
else if (render)
RenderPreviews(i, i + 1);
RenderLibraryPreviews(i, i + 1);
}
return i;
@ -532,7 +532,7 @@ void FractoriumEmberController<T>::PasteXmlAppend()
}
b.clear();
StopPreviewRender();
StopAllPreviewRenderers();
parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", embers);
errors = parser.ErrorReportString();
@ -586,7 +586,7 @@ void FractoriumEmberController<T>::PasteXmlOver()
}
b.clear();
StopPreviewRender();
StopAllPreviewRenderers();
m_EmberFile.m_Embers.clear();//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", m_EmberFile.m_Embers);
errors = parser.ErrorReportString();
@ -812,14 +812,14 @@ void Fractorium::OnActionClearFlame(bool checked) { m_Controller->ClearFlame();
/// </summary>
void Fractorium::OnActionRenderPreviews(bool checked)
{
m_Controller->RenderPreviews();
m_Controller->RenderLibraryPreviews();
}
/// <summary>
/// Stop all previews from being rendered. This is handy if the user
/// opens a large file with many embers in it, such as an animation sequence.
/// </summary>
void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopPreviewRender(); }
void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopLibraryPreviewRender(); }
/// <summary>
/// Show the final render dialog as a modeless dialog to allow
@ -832,10 +832,10 @@ void Fractorium::OnActionFinalRender(bool checked)
{
//First completely stop what the current rendering process is doing.
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
m_Controller->StopPreviewRender();
m_Controller->StopAllPreviewRenderers();
m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file.
m_RenderStatusLabel->setText("Renderer stopped.");
m_FinalRenderDialog->show();
m_FinalRenderDialog->Show(false);
}
/// <summary>

View File

@ -24,7 +24,7 @@ void Fractorium::InitParamsUI()
//Color.
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.05, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaSpin, spinHeight, 1, 9999, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_VibrancySpin, spinHeight, 0, 30, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 0.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_HighlightSpin, spinHeight, 1.0, 10, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHighlightPowerChanged(double)), true, -1.0, -1.0, -1.0);
m_GammaThresholdSpin->setDecimals(4);
@ -469,19 +469,17 @@ void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& tex
template <typename T>
void FractoriumEmberController<T>::DEFilterMinRadiusWidthChanged(double d)
{
if (m_Fractorium->m_DEFilterMinRadiusSpin->value() > m_Fractorium->m_DEFilterMaxRadiusSpin->value())
{
m_Fractorium->m_DEFilterMinRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMaxRadiusSpin->value());
return;
}
UpdateAll([&](Ember<T>& ember)
{
ember.m_MinRadDE = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
}
void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFilterMinRadiusWidthChanged(d); }
void Fractorium::OnDEFilterMinRadiusWidthChanged(double d)
{
if (!ConstrainLow(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin))
m_Controller->DEFilterMinRadiusWidthChanged(d);
}
/// <summary>
/// Set the density estimation filter max radius value.
@ -491,19 +489,17 @@ void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFil
template <typename T>
void FractoriumEmberController<T>::DEFilterMaxRadiusWidthChanged(double d)
{
if (m_Fractorium->m_DEFilterMaxRadiusSpin->value() < m_Fractorium->m_DEFilterMinRadiusSpin->value())
{
m_Fractorium->m_DEFilterMaxRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMinRadiusSpin->value());
return;
}
UpdateAll([&](Ember<T>& ember)
{
ember.m_MaxRadDE = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
}
void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d) { m_Controller->DEFilterMaxRadiusWidthChanged(d); }
void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d)
{
if (!ConstrainHigh(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin))
m_Controller->DEFilterMaxRadiusWidthChanged(d);
}
/// <summary>
/// Set the density estimation filter curve value.

View File

@ -85,4 +85,6 @@
using namespace std;
using namespace EmberNs;
using namespace EmberCLns;
using namespace EmberCommon;
#endif

View File

@ -580,13 +580,11 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, cons
else
m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG);
if ((m_Renderer->EarlyClip() != m_PreviewRenderer->EarlyClip()) ||
(m_Renderer->YAxisUp() != m_PreviewRenderer->YAxisUp()))
if ((m_Renderer->EarlyClip() != m_LibraryPreviewRenderer->EarlyClip()) ||
(m_Renderer->YAxisUp() != m_LibraryPreviewRenderer->YAxisUp()) ||
(m_Renderer->Transparency() != m_LibraryPreviewRenderer->Transparency()))
{
StopPreviewRender();
m_PreviewRenderer->EarlyClip(m_Renderer->EarlyClip());
m_PreviewRenderer->YAxisUp(m_Renderer->YAxisUp());
RenderPreviews();
RenderLibraryPreviews();
}
m_FailedRenders = 0;
@ -690,7 +688,7 @@ bool Fractorium::CreateControllerFromOptions()
if (m_Controller.get())
{
scale = m_Controller->LockedScale();
m_Controller->StopPreviewRender();//Must stop any previews first, else changing controllers will crash the program and SaveCurrentToOpenedFile() will return 0.
m_Controller->StopAllPreviewRenderers();//Must stop any previews first, else changing controllers will crash the program and SaveCurrentToOpenedFile() will return 0.
current = m_Controller->SaveCurrentToOpenedFile(false);
m_Controller->CopyTempPalette(tempPalette);//Convert float to double or save double verbatim;
//Replace below with this once LLVM fixes a crash in their compiler with default lambda parameters.//TODO
@ -698,10 +696,10 @@ bool Fractorium::CreateControllerFromOptions()
//m_Controller->CopyEmberFile(efd);
#ifdef DO_DOUBLE
m_Controller->CopyEmber(ed, [&](Ember<double>& ember) { });
m_Controller->CopyEmberFile(efd, [&](Ember<double>& ember) { });
m_Controller->CopyEmberFile(efd, false, [&](Ember<double>& ember) { });
#else
m_Controller->CopyEmber(ed, [&](Ember<float>& ember) { });
m_Controller->CopyEmberFile(efd, [&](Ember<float>& ember) { });
m_Controller->CopyEmberFile(efd, false, [&](Ember<float>& ember) { });
#endif
m_Controller->Shutdown();
}
@ -718,7 +716,7 @@ bool Fractorium::CreateControllerFromOptions()
if (m_Controller.get())
{
ed.m_Palette = tempPalette;//Restore base temp palette. Adjustments will be then be applied and stored back in in m_Ember.m_Palette below.
m_Controller->SetEmberFile(efd);
m_Controller->SetEmberFile(efd, true);
m_Controller->SetEmber(current, true);
m_Controller->LockedScale(scale);
//Setting these and updating the GUI overwrites the work of clearing them done in SetEmber() above.

View File

@ -134,6 +134,51 @@ void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH,
uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); }
void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); }
/// <summary>
/// Sequence generation settings.
/// </summary>
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).toUInt(); }
void FractoriumSettings::Rotations(uint i) { setValue(ROTATIONS, i); }
uint FractoriumSettings::RotationsMax() { return value(ROTATIONSMAX).toUInt(); }
void FractoriumSettings::RotationsMax(uint i) { setValue(ROTATIONSMAX, i); }
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); }
/// <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>

View File

@ -21,12 +21,19 @@
#define OPENCLSUBBATCH "render/openclsubbatch"
#define RANDOMCOUNT "render/randomcount"
#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 VARFILTERSUM "varfilter/sumcheckbox"
#define VARFILTERASSIGN "varfilter/assigncheckbox"
#define VARFILTERPPSUM "varfilter/ppsumcheckbox"
#define VARFILTERPPASSIGN "varfilter/ppassigncheckbox"
#define VARFILTERSDC "varfilter/dccheckbox"
#define VARFILTERSSTATE "varfilter/statecheckbox"
#define VARFILTERSTATE "varfilter/statecheckbox"
#define VARFILTERPARAM "varfilter/paramcheckbox"
#define VARFILTERNONPARAM "varfilter/nonparamcheckbox"
@ -126,6 +133,43 @@ public:
uint RandomCount();
void RandomCount(uint i);
uint FramesPerRot();
void FramesPerRot(uint i);
uint FramesPerRotMax();
void FramesPerRotMax(uint i);
uint Rotations();
void Rotations(uint i);
uint RotationsMax();
void RotationsMax(uint i);
uint BlendFrames();
void BlendFrames(uint i);
uint BlendFramesMax();
void BlendFramesMax(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);

View File

@ -26,14 +26,14 @@ FractoriumVariationsDialog::FractoriumVariationsDialog(FractoriumSettings* setti
m_CheckBoxes.push_back(ui.StateCheckBox);
m_CheckBoxes.push_back(ui.ParamCheckBox);
m_CheckBoxes.push_back(ui.NonParamCheckBox);
ui.SumCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSUM).toInt()));
ui.AssignCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERASSIGN).toInt()));
ui.PpSumCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPPSUM).toInt()));
ui.PpAssignCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPPASSIGN).toInt()));
ui.DcCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSDC).toInt()));
ui.StateCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSSTATE).toInt()));
ui.ParamCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPARAM).toInt()));
ui.NonParamCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERNONPARAM).toInt()));
ui.SumCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterSum ()));
ui.AssignCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterAssign ()));
ui.PpSumCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterPpsum ()));
ui.PpAssignCheckBox->setCheckState(Qt::CheckState(m_Settings->VarFilterPpassign()));
ui.DcCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterSdc ()));
ui.StateCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterState ()));
ui.ParamCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterParam ()));
ui.NonParamCheckBox->setCheckState(Qt::CheckState(m_Settings->VarFilterNonparam()));
for (auto& cb : m_CheckBoxes)
{
@ -112,14 +112,14 @@ void FractoriumVariationsDialog::SyncSettings()
m[cb->text()] = cb->checkState() == Qt::CheckState::Checked;
});
m_Settings->Variations(m);
m_Settings->setValue(VARFILTERSUM , int(ui.SumCheckBox->checkState()));
m_Settings->setValue(VARFILTERASSIGN , int(ui.AssignCheckBox->checkState()));
m_Settings->setValue(VARFILTERPPSUM , int(ui.PpSumCheckBox->checkState()));
m_Settings->setValue(VARFILTERPPASSIGN, int(ui.PpAssignCheckBox->checkState()));
m_Settings->setValue(VARFILTERSDC , int(ui.DcCheckBox->checkState()));
m_Settings->setValue(VARFILTERSSTATE , int(ui.StateCheckBox->checkState()));
m_Settings->setValue(VARFILTERPARAM , int(ui.ParamCheckBox->checkState()));
m_Settings->setValue(VARFILTERNONPARAM, int(ui.NonParamCheckBox->checkState()));
m_Settings->VarFilterSum (int(ui.SumCheckBox->checkState()));
m_Settings->VarFilterAssign (int(ui.AssignCheckBox->checkState()));
m_Settings->VarFilterPpsum (int(ui.PpSumCheckBox->checkState()));
m_Settings->VarFilterPpassign(int(ui.PpAssignCheckBox->checkState()));
m_Settings->VarFilterSdc (int(ui.DcCheckBox->checkState()));
m_Settings->VarFilterState (int(ui.StateCheckBox->checkState()));
m_Settings->VarFilterParam (int(ui.ParamCheckBox->checkState()));
m_Settings->VarFilterNonparam(int(ui.NonParamCheckBox->checkState()));
}
/// <summary>

View File

@ -243,6 +243,20 @@
</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>