diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h
index 99e5e73..a68f0ad 100644
--- a/Source/Ember/SheepTools.h
+++ b/Source/Ember/SheepTools.h
@@ -1323,6 +1323,16 @@ public:
m_Comment = comment;
}
+ ///
+ /// Set stagger value.
+ /// Greater than 0 means interpolate xforms one at a time, else interpolate all at once.
+ ///
+ /// The stagger value to set.
+ void Stagger(T stagger)
+ {
+ m_Stagger = stagger;
+ }
+
private:
bool m_Smooth = true;
intmax_t m_SheepGen = -1;
diff --git a/Source/EmberAnimate/EmberAnimate.cpp b/Source/EmberAnimate/EmberAnimate.cpp
index ac99d72..f97ac38 100644
--- a/Source/EmberAnimate/EmberAnimate.cpp
+++ b/Source/EmberAnimate/EmberAnimate.cpp
@@ -2,6 +2,8 @@
#include "EmberAnimate.h"
#include "JpegUtils.h"
+using namespace EmberCommon;
+
///
/// 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("");
diff --git a/Source/EmberCommon/EmberCommon.h b/Source/EmberCommon/EmberCommon.h
index 9974b15..134a6c9 100644
--- a/Source/EmberCommon/EmberCommon.h
+++ b/Source/EmberCommon/EmberCommon.h
@@ -7,6 +7,8 @@
/// Ember and its derivatives.
///
+namespace EmberCommon
+{
///
/// 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;
}
+///
+/// Formats a filename with digits using the passed in amount of 0 padding.
+///
+/// The ember whose name will be set
+/// The ostringstream which will be used to format
+/// The amount of padding to use
+template
+void FormatName(Ember& result, ostringstream& os, streamsize padding)
+{
+ os << std::setw(padding) << result.m_Time;
+ result.m_Name = os.str();
+ os.str("");
+}
+
///
/// 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& rgba, vector& 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>> CreateRenderers(eRendererType rend
else
{
s = "CPU";
- v.push_back(std::move(unique_ptr>(::CreateRenderer(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
+ v.push_back(std::move(unique_ptr>(EmberCommon::CreateRenderer(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
}
}
catch (const std::exception& e)
@@ -388,7 +404,7 @@ static vector>> CreateRenderers(eRendererType rend
try
{
s = "CPU";
- v.push_back(std::move(unique_ptr>(::CreateRenderer(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
+ v.push_back(std::move(unique_ptr>(EmberCommon::CreateRenderer(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
}
catch (const std::exception& e)
{
@@ -628,6 +644,7 @@ static vector*> FindVarsWithout(const vector
/// Simple macro to print a string if the --verbose options has been specified.
diff --git a/Source/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h
index f5f745a..0d7f384 100644
--- a/Source/EmberCommon/EmberOptions.h
+++ b/Source/EmberCommon/EmberOptions.h
@@ -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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= Name of the input file.\n"));
- INITSTRINGOPTION(Out, Eos(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_OUT, _T("--out"), "", SO_REQ_SEP, "\t--out= 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= 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= 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= 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= 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= ID to use in tags / image comments.\n"));
- INITSTRINGOPTION(Url, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_URL, _T("--url"), "", SO_REQ_SEP, "\t--url= URL to use in tags / image comments.\n"));
- INITSTRINGOPTION(Nick, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_NICK, _T("--nick"), "", SO_REQ_SEP, "\t--nick= Nickname to use in tags / image comments.\n"));
- INITSTRINGOPTION(Comment, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_COMMENT, _T("--comment"), "", SO_REQ_SEP, "\t--comment= Comment to use in tags.\n"));
- INITSTRINGOPTION(TemplateFile, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_TEMPLATE, _T("--template"), "", SO_REQ_SEP, "\t--template= Apply defaults based on this flame.\n"));
- INITSTRINGOPTION(Clone, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE, _T("--clone"), "", SO_REQ_SEP, "\t--clone= 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= 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= 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= 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= 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= 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= ...with one flame from this file.\n"));
- INITSTRINGOPTION(Method, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_METHOD, _T("--method"), "", SO_REQ_SEP, "\t--method= 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= Interpolate the input file.\n"));
- INITSTRINGOPTION(Rotate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ROTATE, _T("--rotate"), "", SO_REQ_SEP, "\t--rotate= Rotate the input file.\n"));
- INITSTRINGOPTION(Sequence, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SEQUENCE, _T("--sequence"), "", SO_REQ_SEP, "\t--sequence= 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= 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= 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= 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= 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= Name of the input file.\n"));
+ INITSTRINGOPTION(Out, Eos(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_OUT, _T("--out"), "", SO_REQ_SEP, "\t--out= 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= 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= 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= 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= 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= ID to use in tags / image comments.\n"));
+ INITSTRINGOPTION(Url, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_URL, _T("--url"), "", SO_REQ_SEP, "\t--url= URL to use in tags / image comments.\n"));
+ INITSTRINGOPTION(Nick, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_NICK, _T("--nick"), "", SO_REQ_SEP, "\t--nick= Nickname to use in tags / image comments.\n"));
+ INITSTRINGOPTION(Comment, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_COMMENT, _T("--comment"), "", SO_REQ_SEP, "\t--comment= Comment to use in tags.\n"));
+ INITSTRINGOPTION(TemplateFile, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_TEMPLATE, _T("--template"), "", SO_REQ_SEP, "\t--template= Apply defaults based on this flame.\n"));
+ INITSTRINGOPTION(Clone, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CLONE, _T("--clone"), "", SO_REQ_SEP, "\t--clone= 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= 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= 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= 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= 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= 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= ...with one flame from this file.\n"));
+ INITSTRINGOPTION(Method, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_METHOD, _T("--method"), "", SO_REQ_SEP, "\t--method= 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= Interpolate the input file.\n"));
+ INITSTRINGOPTION(Rotate, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ROTATE, _T("--rotate"), "", SO_REQ_SEP, "\t--rotate= Rotate the input file.\n"));
+ INITSTRINGOPTION(Sequence, Eos(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SEQUENCE, _T("--sequence"), "", SO_REQ_SEP, "\t--sequence= 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= 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= 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= Extra attributes to place in the flame section of the Xml.\n"));
}
///
@@ -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;
diff --git a/Source/EmberGenome/EmberGenome.cpp b/Source/EmberGenome/EmberGenome.cpp
index f34741b..0ec8308 100644
--- a/Source/EmberGenome/EmberGenome.cpp
+++ b/Source/EmberGenome/EmberGenome.cpp
@@ -3,6 +3,8 @@
#include "EmberGenome.h"
#include "JpegUtils.h"
+using namespace EmberCommon;
+
///
/// Set various default test values on the passed in ember.
///
@@ -34,14 +36,6 @@ void SetDefaultTestValues(Ember& ember)
ember.m_CurveDE = T(0.6);
}
-template
-void FormatName(Ember& result, ostringstream& os, streamsize padding)
-{
- os << std::setw(padding) << result.m_Time;
- result.m_Name = os.str();
- os.str("");
-}
-
///
/// 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");
diff --git a/Source/EmberRender/EmberRender.cpp b/Source/EmberRender/EmberRender.cpp
index 9d3ac1a..6be2ec4 100644
--- a/Source/EmberRender/EmberRender.cpp
+++ b/Source/EmberRender/EmberRender.cpp
@@ -2,7 +2,7 @@
#include "EmberRender.h"
#include "JpegUtils.h"
-//template weak_ptr Singleton::m_Instance = weak_ptr();
+using namespace EmberCommon;
///
/// 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();
}
diff --git a/Source/EmberTester/EmberTester.cpp b/Source/EmberTester/EmberTester.cpp
index 809fc98..a411266 100644
--- a/Source/EmberTester/EmberTester.cpp
+++ b/Source/EmberTester/EmberTester.cpp
@@ -13,6 +13,7 @@
///
using namespace EmberNs;
+using namespace EmberCommon;
template
void SaveFinalImage(Renderer& renderer, vector& pixels, char* suffix)
diff --git a/Source/Fractorium/EmberFile.h b/Source/Fractorium/EmberFile.h
index fc3d78a..f4788a7 100644
--- a/Source/Fractorium/EmberFile.h
+++ b/Source/Fractorium/EmberFile.h
@@ -159,9 +159,9 @@ public:
/// Return the default filename based on the current date/time.
///
/// The default filename
- 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");
}
///
diff --git a/Source/Fractorium/FinalRenderDialog.cpp b/Source/Fractorium/FinalRenderDialog.cpp
index ae629e6..eeaa7ab 100644
--- a/Source/Fractorium/FinalRenderDialog.cpp
+++ b/Source/Fractorium/FinalRenderDialog.cpp
@@ -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);
}
+///
+/// Show the final render dialog and specify whether it was called from the toolbar or the sequence render button.
+///
+/// True if this is called from the sequence render button, else false.
+void FractoriumFinalRenderDialog::Show(bool fromSequence)
+{
+ m_FromSequence = fromSequence;
+ show();
+}
+
///
/// GUI settings wrapper functions, getters only.
///
@@ -636,7 +646,7 @@ void FractoriumFinalRenderDialog::OnCancelRenderClicked(bool checked)
/// The event
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 ed;
EmberFile efi;
- m_Fractorium->m_Controller->CopyEmberFile(efi, [&](Ember& ember)
+ m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember& 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 ed;
EmberFile efi;
- m_Fractorium->m_Controller->CopyEmberFile(efi, [&](Ember& ember)
+ m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember& 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& ember) { });//Convert float to double or save double verbatim;
+ m_Controller->CopyEmberFile(efd, false, [&](Ember& ember) { });//Convert float to double or save double verbatim;
#else
- m_Controller->CopyEmberFile(efd, [&](Ember& ember) { });//Convert float to double or save double verbatim;
+ m_Controller->CopyEmberFile(efd, false, [&](Ember& 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);
}
}
diff --git a/Source/Fractorium/FinalRenderDialog.h b/Source/Fractorium/FinalRenderDialog.h
index 6e5874b..3c01abb 100644
--- a/Source/Fractorium/FinalRenderDialog.h
+++ b/Source/Fractorium/FinalRenderDialog.h
@@ -40,13 +40,18 @@ class FractoriumFinalRenderDialog : public QDialog
friend Fractorium;
friend FinalRenderEmberControllerBase;
friend FinalRenderEmberController;
+ friend PreviewRenderer;
+ friend FinalRenderPreviewRenderer;
#ifdef DO_DOUBLE
friend FinalRenderEmberController;
+ friend PreviewRenderer;
+ friend FinalRenderPreviewRenderer;
#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;
diff --git a/Source/Fractorium/FinalRenderEmberController.cpp b/Source/Fractorium/FinalRenderEmberController.cpp
index fa35bff..446de21 100644
--- a/Source/Fractorium/FinalRenderEmberController.cpp
+++ b/Source/Fractorium/FinalRenderEmberController.cpp
@@ -101,50 +101,7 @@ template
FinalRenderEmberController::FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender)
: FinalRenderEmberControllerBase(finalRender)
{
- m_FinalPreviewRenderer = make_unique>();
- 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(1, std::min(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasW)));//Ensure neither is zero.
- m_PreviewEmber.m_FinalRasH = std::max(1, std::min(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(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& 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>(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::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.
///
-template void FinalRenderEmberController::SetEmberFile(const EmberFile& emberFile)
+template void FinalRenderEmberController::SetEmberFile(const EmberFile& emberFile, bool move)
{
- m_EmberFile = emberFile;
+ move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile;
m_Ember = m_EmberFile.Get(0);
}
-template void FinalRenderEmberController::CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation)
+template void FinalRenderEmberController::CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation)
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
#ifdef DO_DOUBLE
-template void FinalRenderEmberController::SetEmberFile(const EmberFile& emberFile)
+template void FinalRenderEmberController::SetEmberFile(const EmberFile& emberFile, bool move)
{
- m_EmberFile = emberFile;
+ move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile;
m_Ember = m_EmberFile.Get(0);
}
-template void FinalRenderEmberController::CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation)
+template void FinalRenderEmberController::CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation)
{
emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
@@ -692,8 +649,7 @@ tuple FinalRenderEmberController::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 FinalRenderEmberController::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* FinalRenderEmberController::FirstOrDefaultRender
}
}
-///
-/// Stop the preview renderer.
-/// This is meant to only be called programatically and never by the user.
-///
-template
-void FinalRenderEmberController::CancelPreviewRender()
-{
- m_FinalPreviewRenderer->Abort();
-
- while (m_FinalPreviewRenderer->InRender()) { QApplication::processEvents(); }
-
- while (m_PreviewRun) { QApplication::processEvents(); }
-
- while (m_FinalPreviewResult.isRunning()) { QApplication::processEvents(); }
-}
-
///
/// Save the output of the render.
///
@@ -1017,6 +956,56 @@ QString FinalRenderEmberController::CheckMemory(const tuple
+/// 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.
+///
+/// Ignored
+/// Ignored
+template
+void FinalRenderPreviewRenderer::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(1, std::min(maxDim, size_t(scalePercentage * e->m_FinalRasW)));//Ensure neither is zero.
+ m_PreviewEmber.m_FinalRasH = std::max(1, std::min(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(&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& 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;
#ifdef DO_DOUBLE
diff --git a/Source/Fractorium/FinalRenderEmberController.h b/Source/Fractorium/FinalRenderEmberController.h
index 05e21e3..86b5a10 100644
--- a/Source/Fractorium/FinalRenderEmberController.h
+++ b/Source/Fractorium/FinalRenderEmberController.h
@@ -13,6 +13,7 @@
///
class Fractorium;
class FractoriumFinalRenderDialog;
+template class FinalRenderPreviewRenderer;
///
/// 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 m_FinishedImageCount;
QFuture m_Result;
- QFuture m_FinalPreviewResult;
std::function m_FinalRenderFunc;
- std::function 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
class FinalRenderEmberController : public FinalRenderEmberControllerBase
{
+ friend FinalRenderPreviewRenderer;
+
public:
FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender);
virtual ~FinalRenderEmberController() { }
//Virtual functions overridden from FractoriumEmberControllerBase.
- virtual void SetEmberFile(const EmberFile& emberFile) override;
- virtual void CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
+ virtual void SetEmberFile(const EmberFile& emberFile, bool move) override;
+ virtual void CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
#ifdef DO_DOUBLE
- virtual void SetEmberFile(const EmberFile& emberFile) override;
- virtual void CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
+ virtual void SetEmberFile(const EmberFile& emberFile, bool move) override;
+ virtual void CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
#endif
virtual void SetEmber(size_t index, bool verbatim) override;
virtual bool Render() override;
@@ -132,7 +132,6 @@ public:
EmberNs::Renderer* FirstOrDefaultRenderer();
protected:
- void CancelPreviewRender();
void HandleFinishedProgress();
void SaveCurrentRender(Ember& ember);
void SaveCurrentRender(Ember& ember, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, size_t channels, size_t bpc);
@@ -143,10 +142,32 @@ protected:
void SetProgressComplete(int val);
Ember* m_Ember;
- Ember m_PreviewEmber;
EmberFile m_EmberFile;
EmberToXml m_XmlWriter;
- unique_ptr> m_FinalPreviewRenderer;
+ unique_ptr> m_FinalPreviewRenderer;
vector>> m_Renderers;
};
+///
+/// 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.
+///
+template
+class FinalRenderPreviewRenderer : public PreviewRenderer
+{
+public:
+ using PreviewRenderer::m_PreviewRun;
+ using PreviewRenderer::m_PreviewEmber;
+ using PreviewRenderer::m_PreviewRenderer;
+ using PreviewRenderer::m_PreviewFinalImage;
+
+ FinalRenderPreviewRenderer(FinalRenderEmberController* controller) : m_Controller(controller)
+ {
+ }
+
+ virtual void PreviewRenderFunc(uint start, uint end) override;
+
+private:
+ FinalRenderEmberController* m_Controller;
+};
diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp
index a4f09de..d7eb312 100644
--- a/Source/Fractorium/Fractorium.cpp
+++ b/Source/Fractorium/Fractorium.cpp
@@ -198,6 +198,7 @@ Fractorium::Fractorium(QWidget* p)
///
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
///
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);
diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h
index c47c743..fea62e3 100644
--- a/Source/Fractorium/Fractorium.h
+++ b/Source/Fractorium/Fractorium.h
@@ -75,17 +75,24 @@ class Fractorium : public QMainWindow
friend FractoriumEmberController;
friend FinalRenderEmberControllerBase;
friend FinalRenderEmberController;
+ friend PreviewRenderer;
+ friend TreePreviewRenderer;
#ifdef DO_DOUBLE
friend GLEmberController;
friend FractoriumEmberController;
friend FinalRenderEmberController;
+ friend PreviewRenderer;
+ friend TreePreviewRenderer;
#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& 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 GetCurrentEmberIndex();
+ void SyncSequenceSettings();
//Params.
diff --git a/Source/Fractorium/Fractorium.ui b/Source/Fractorium/Fractorium.ui
index 610b9ce..86719ae 100644
--- a/Source/Fractorium/Fractorium.ui
+++ b/Source/Fractorium/Fractorium.ui
@@ -74,7 +74,7 @@
0
0
- 1184
+ 1016
987
@@ -6683,7 +6683,7 @@
150
- 200
+ 251
@@ -6711,6 +6711,12 @@
1
+
+
+ 0
+ 0
+
+
false
@@ -6731,96 +6737,557 @@
4
-
-
-
- QFrame::NoFrame
+
+
+
+ 0
+ 0
+
-
- QFrame::Raised
+
+ Qt::Vertical
-
- 1
-
-
- true
-
-
-
-
- 0
- 0
- 256
- 956
-
+
+
+ Qt::WheelFocus
-
-
- 0
+
+ Qt::DefaultContextMenu
+
+
+ false
+
+
+ QFrame::Panel
+
+
+ QFrame::Plain
+
+
+ QAbstractItemView::SelectedClicked
+
+
+ true
+
+
+ false
+
+
+ QAbstractItemView::InternalMove
+
+
+ Qt::MoveAction
+
+
+ QAbstractItemView::SelectRows
+
+
+ 10
+
+
+ false
+
+
+ 27
+
+
+
+ Current Flame File
-
- 0
+
+
+
+
+ QFrame::NoFrame
+
+
+ 0
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 424
+ 589
+
-
- 0
+
+
+ 0
+ 0
+
-
- 0
-
-
- 0
-
-
-
-
-
- Qt::WheelFocus
-
-
- Qt::DefaultContextMenu
-
-
- false
-
-
- QFrame::Panel
-
-
- QFrame::Plain
-
-
- QAbstractItemView::SelectedClicked
-
-
- true
-
-
- false
-
-
- QAbstractItemView::InternalMove
-
-
- Qt::MoveAction
-
-
- QAbstractItemView::SelectRows
-
-
- 10
-
-
- false
-
-
- 27
-
-
-
- Current Flame File
+
+
+ 4
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ 4
-
-
-
-
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Frames per rot:
+
+
+ 1
+
+
+ 10000
+
+
+ 30
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Stagger
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Rotations:
+
+
+ 10000.000000000000000
+
+
+ 3.000000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Frames per rot max:
+
+
+ 1
+
+
+ 10000
+
+
+ 30
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::LeftToRight
+
+
+ Random
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractSpinBox::NoButtons
+
+
+
+
+
+ Blend frames:
+
+
+ 10000
+
+
+ 120
+
+
+
+ -
+
+
+ false
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Blend max frames:
+
+
+ 10000
+
+
+ 120
+
+
+
+ -
+
+
+ false
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Rotations max:
+
+
+ 10000.000000000000000
+
+
+ 3.000000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 125
+ 16777215
+
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Start flame:
+
+
+ 999999999
+
+
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Stop flame:
+
+
+ 999999999
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 33
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ All
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ true
+
+
+ QAbstractSpinBox::NoButtons
+
+
+ Start count:
+
+
+ 999999999
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Start Previews
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Stop Previews
+
+
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+ Generate
+
+
+
+ -
+
+
+ Render
+
+
+
+ -
+
+
+ Save
+
+
+
+ -
+
+
+ Open
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ Qt::WheelFocus
+
+
+ Qt::DefaultContextMenu
+
+
+ false
+
+
+ QFrame::Panel
+
+
+ QFrame::Plain
+
+
+ QAbstractItemView::SelectedClicked
+
+
+ true
+
+
+ false
+
+
+ QAbstractItemView::InternalMove
+
+
+ Qt::MoveAction
+
+
+ QAbstractItemView::SelectRows
+
+
+ 10
+
+
+ false
+
+
+ 27
+
+
+
+ Sequence
+
+
+
+
+
+
diff --git a/Source/Fractorium/FractoriumCommon.h b/Source/Fractorium/FractoriumCommon.h
index f5d9cf0..43f6554 100644
--- a/Source/Fractorium/FractoriumCommon.h
+++ b/Source/Fractorium/FractoriumCommon.h
@@ -406,3 +406,47 @@ static QList GetAllParents(QWidget* widget)
return parents;
}
+
+///
+/// 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.
+///
+/// The control which must contain the lower value
+/// The control which must contain the higher value
+/// True if the value of low had to be changed, else false.
+template
+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;
+}
+
+///
+/// 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.
+///
+/// The control which must contain the lower value
+/// The control which must contain the higher value
+/// True if the value of high had to be changed, else false.
+template
+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;
+}
diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp
index 6435215..99e13e6 100644
--- a/Source/Fractorium/FractoriumEmberController.cpp
+++ b/Source/Fractorium/FractoriumEmberController.cpp
@@ -44,6 +44,8 @@ FractoriumEmberController::FractoriumEmberController(Fractorium* fractorium)
{
bool b = false;
m_GLController = make_unique>(fractorium, fractorium->ui.GLDisplay, this);
+ m_LibraryPreviewRenderer = make_unique>(this, m_Fractorium->ui.LibraryTree, m_EmberFile);
+ m_SequencePreviewRenderer = make_unique>(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 paths =
@@ -70,57 +72,6 @@ FractoriumEmberController::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 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*>(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(treeItem)),
- Q_ARG(vector&, m_PreviewFinalImage),
- Q_ARG(uint, PREVIEW_SIZE),
- Q_ARG(uint, PREVIEW_SIZE));
- }
- }
- }
- }
-
- m_PreviewRun = false;
- m_PreviewRunning = false;
- };
}
///
@@ -136,11 +87,19 @@ FractoriumEmberController::~FractoriumEmberController() { }
///
template void FractoriumEmberController::SetEmber(const Ember& ember, bool verbatim, bool updatePointer) { SetEmberPrivate(ember, verbatim, updatePointer); }
template void FractoriumEmberController::CopyEmber(Ember& ember, std::function& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
-template void FractoriumEmberController::SetEmberFile(const EmberFile& emberFile) { m_EmberFile = emberFile; }
-template void FractoriumEmberController::CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation)
+template void FractoriumEmberController::SetEmberFile(const EmberFile& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
+template void FractoriumEmberController::CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& 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 void FractoriumEmberController::SetTempPalette(const Palette& palette) { m_TempPalette = palette; }
@@ -148,11 +107,19 @@ template void FractoriumEmberController::CopyTempPalette(Palette
#ifdef DO_DOUBLE
template void FractoriumEmberController::SetEmber(const Ember& ember, bool verbatim, bool updatePointer) { SetEmberPrivate(ember, verbatim, updatePointer); }
template void FractoriumEmberController::CopyEmber(Ember& ember, std::function& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
-template void FractoriumEmberController::SetEmberFile(const EmberFile& emberFile) { m_EmberFile = emberFile; }
-template void FractoriumEmberController::CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation)
+template void FractoriumEmberController::SetEmberFile(const EmberFile& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
+template void FractoriumEmberController::CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& 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 void FractoriumEmberController::SetTempPalette(const Palette& palette) { m_TempPalette = palette; }
@@ -387,8 +354,58 @@ void FractoriumEmberController::SetEmberPrivate(const Ember& ember, bool v
m_Fractorium->CenterScrollbars();
}
+///
+/// Thin derivation to handle preview rendering multiple embers previews to a tree.
+///
+/// The 0-based index to start rendering previews for
+/// The 0-based index which is one beyond the last ember to render a preview for
+template
+void TreePreviewRenderer::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(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&, m_PreviewFinalImage),
+ Q_ARG(uint, PREVIEW_SIZE),
+ Q_ARG(uint, PREVIEW_SIZE));
+ }
+ }
+ }
+ }
+}
+
template class FractoriumEmberController;
+template class PreviewRenderer;
+template class TreePreviewRenderer;
#ifdef DO_DOUBLE
template class FractoriumEmberController;
+ template class PreviewRenderer;
+ template class TreePreviewRenderer;
#endif
diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h
index 3866526..44c46ff 100644
--- a/Source/Fractorium/FractoriumEmberController.h
+++ b/Source/Fractorium/FractoriumEmberController.h
@@ -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.
///
class Fractorium;
+template class PreviewRenderer;
+template class TreePreviewRenderer;
+
#define PREVIEW_SIZE 256
#define UNDO_SIZE 128
@@ -54,20 +57,19 @@ public:
//Embers.
virtual void SetEmber(const Ember& ember, bool verbatim, bool updatePointer) { }
virtual void CopyEmber(Ember& ember, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) { }//Uncomment default lambdas once LLVM fixes a crash in their compiler with default lambda parameters.//TODO
- virtual void SetEmberFile(const EmberFile& emberFile) { }
- virtual void CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) { }
+ virtual void SetEmberFile(const EmberFile& emberFile, bool move) { }
+ virtual void CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) { }
virtual void SetTempPalette(const Palette& palette) { }
virtual void CopyTempPalette(Palette& palette) { }
#ifdef DO_DOUBLE
virtual void SetEmber(const Ember& ember, bool verbatim, bool updatePointer) { }
virtual void CopyEmber(Ember& ember, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) { }
- virtual void SetEmberFile(const EmberFile& emberFile) { }
- virtual void CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) { }
+ virtual void SetEmberFile(const EmberFile& emberFile, bool move) { }
+ virtual void CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) { }
virtual void SetTempPalette(const Palette& palette) { }
virtual void CopyTempPalette(Palette& 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& 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
class FractoriumEmberController : public FractoriumEmberControllerBase
{
+ friend PreviewRenderer;
+ friend TreePreviewRenderer;
+
public:
FractoriumEmberController(Fractorium* fractorium);
FractoriumEmberController(const FractoriumEmberController& controller) = delete;
@@ -295,20 +308,19 @@ public:
//Embers.
virtual void SetEmber(const Ember& ember, bool verbatim, bool updatePointer) override;
virtual void CopyEmber(Ember& ember, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
- virtual void SetEmberFile(const EmberFile& emberFile) override;
- virtual void CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
+ virtual void SetEmberFile(const EmberFile& emberFile, bool move) override;
+ virtual void CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
virtual void SetTempPalette(const Palette& palette) override;
virtual void CopyTempPalette(Palette& palette) override;
#ifdef DO_DOUBLE
virtual void SetEmber(const Ember& ember, bool verbatim, bool updatePointer) override;
virtual void CopyEmber(Ember& ember, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
- virtual void SetEmberFile(const EmberFile& emberFile) override;
- virtual void CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
+ virtual void SetEmberFile(const EmberFile& emberFile, bool move) override;
+ virtual void CopyEmberFile(EmberFile& emberFile, bool sequence, std::function& ember)> perEmberOperation/* = [&](Ember& ember) { }*/) override;
virtual void SetTempPalette(const Palette& palette) override;
virtual void CopyTempPalette(Palette& 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& 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* renderer, EmberFile& 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 m_TempOpacities;
vector m_NormalizedWeights;
Ember m_Ember;
const void* m_EmberFilePointer = nullptr;
EmberFile m_EmberFile;
+ EmberFile m_SequenceFile;
deque> m_UndoList;
vector> m_CopiedXforms;
Xform m_CopiedFinalXform;
@@ -519,8 +539,115 @@ private:
shared_ptr> m_VariationList;
unique_ptr> m_SheepTools;
unique_ptr> m_GLController;
- unique_ptr> m_PreviewRenderer = make_unique>();
- QFuture m_PreviewResult;
- std::function m_PreviewRenderFunc;
+ unique_ptr> m_LibraryPreviewRenderer;
+ unique_ptr> m_SequencePreviewRenderer;
};
+///
+/// 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.
+///
+template
+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 m_PreviewEmber;
+ vector m_PreviewFinalImage;
+ EmberNs::Renderer m_PreviewRenderer;
+
+private:
+ QFuture m_PreviewResult;
+ std::recursive_mutex m_PreviewCs;
+};
+
+///
+/// Thin derivation to handle preview rendering multiple embers previews to a tree.
+///
+template
+class TreePreviewRenderer : public PreviewRenderer
+{
+public:
+ using PreviewRenderer::m_PreviewRun;
+ using PreviewRenderer::m_PreviewEmber;
+ using PreviewRenderer::m_PreviewRenderer;
+ using PreviewRenderer::m_PreviewFinalImage;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A pointer to the controller this instance is a member of
+ /// A pointer to the tree to render to
+ /// A reference to the ember file to render
+ TreePreviewRenderer(FractoriumEmberController* controller, QTreeWidget* tree, EmberFile& 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* m_Controller;
+ QTreeWidget* m_Tree;
+ EmberFile& m_EmberFile;
+};
diff --git a/Source/Fractorium/FractoriumLibrary.cpp b/Source/Fractorium/FractoriumLibrary.cpp
index fe50746..d9cad4d 100644
--- a/Source/Fractorium/FractoriumLibrary.cpp
+++ b/Source/Fractorium/FractoriumLibrary.cpp
@@ -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);
}
///
@@ -95,7 +120,7 @@ void FractoriumEmberController::FillLibraryTree(int selectIndex)
uint i = 0;
auto tree = m_Fractorium->ui.LibraryTree;
vector v(size * size * 4);
- StopPreviewRender();
+ StopAllPreviewRenderers();
tree->clear();
QCoreApplication::flush();
tree->blockSignals(true);
@@ -125,8 +150,9 @@ void FractoriumEmberController::FillLibraryTree(int selectIndex)
if (auto emberItem = dynamic_cast*>(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::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::Delete(const pair&
{
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& p)
/// The 0-based index to start rendering previews for
/// The 0-based index which is one beyond the last ember to render a preview for
template
-void FractoriumEmberController::RenderPreviews(uint start, uint end)
+void FractoriumEmberController::RenderPreviews(QTreeWidget* tree, TreePreviewRenderer* renderer, EmberFile& 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::RenderPreviews(uint start, uint end)
vector emptyPreview(PREVIEW_SIZE * PREVIEW_SIZE * 4);
for (int i = 0; i < childCount; i++)
- if (auto treeItem = dynamic_cast*>(top->child(i)))
+ if (auto treeItem = dynamic_cast(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);
}
///
-/// Stop the preview rendering thread.
+/// Wrapper around calling RenderPreviews with the appropriate values passed in for the previews in the main library tree.
+///
+/// The 0-based index to start rendering previews for
+/// The 0-based index which is one beyond the last ember to render a preview for
+template
+void FractoriumEmberController::RenderLibraryPreviews(uint start, uint end)
+{
+ RenderPreviews(m_Fractorium->ui.LibraryTree, m_LibraryPreviewRenderer.get(), m_EmberFile, start, end);
+}
+
+template
+void FractoriumEmberController::StopLibraryPreviewRender() { m_LibraryPreviewRenderer->Stop(); }
+
+///
+/// Thing wrapper around StopLibraryPreviewRender() and StopSequencePreviewRender() to stop both preview renderers.
///
template
-void FractoriumEmberController::StopPreviewRender()
+void FractoriumEmberController::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);
+///
+/// Fill the sequence tree with the names of the embers in the
+/// currently generated sequence.
+/// Start the sequence preview render thread.
+///
+template
+void FractoriumEmberController::FillSequenceTree()
+{
+ uint size = 64;
+ uint i = 0;
+ auto tree = m_Fractorium->ui.SequenceTree;
+ vector 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();
+}
+
+///
+/// Copy the text of the root item to the name of the sequence file.
+/// Called whenever the text of the root item is changed.
+///
+/// The root sequence tree item which changed
+/// The column clicked, ignored.
+template
+void FractoriumEmberController::SequenceTreeItemChanged(QTreeWidgetItem* item, int col)
+{
+ if (auto parentItem = dynamic_cast(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); }
+
+///
+/// Wrapper around calling RenderPreviews with the appropriate values passed in for the previews in the sequence tree.
+/// Called when Render Previews is clicked.
+///
+/// Ignored, render all.
+/// Ignored, render all.
+template
+void FractoriumEmberController::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(); }
+
+///
+/// Stop rendering the sequence previews.
+/// Called when Stop Previews is clicked.
+///
+template
+void FractoriumEmberController::StopSequencePreviewRender() { m_SequencePreviewRenderer->Stop(); }
+void Fractorium::OnSequenceStopPreviewsButtonClicked(bool checked) { m_Controller->StopSequencePreviewRender(); }
+
+///
+/// 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.
+///
+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(); }
+
+///
+/// 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.
+///
+template
+void FractoriumEmberController::SequenceGenerateButtonClicked()
+{
+ StopAllPreviewRenderers();
+ SaveCurrentToOpenedFile(false);
+ Ember 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> devices;//Dummy.
+ EmberReport emberReport;
+ ostringstream os;
+ string palettePath =
+#ifdef _WIN32
+ "./flam3-palettes.xml";
+#else
+ "~/.config/fractorium";
+#endif
+ SheepTools tools(palettePath, EmberCommon::CreateRenderer(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::DefaultFilename("Sequence_");
+ double blend;
+ size_t frame;
+ Ember 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(rots, rotsMax) : rots;
+ embers[0] = *it;
+
+ if (rotations > 0)
+ {
+ double rotFrames = randFramesRot ? m_Rand.Frand(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(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(); }
+
+///
+/// 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.
+///
+/// Ignored.
+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.
+}
+
+///
+/// Save the sequence to a file.
+///
+template
+void FractoriumEmberController::SequenceSaveButtonClicked()
+{
+ auto s = m_Fractorium->m_Settings;
+ QString filename = m_Fractorium->SetupSaveXmlDialog(m_SequenceFile.m_Filename);
+
+ if (filename != "")
+ {
+ EmberToXml 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(); }
+
+///
+/// Open one or more sequence file, concatenate them all, place them in the sequence
+/// tree and begin rendering the sequence previews.
+///
+template
+void FractoriumEmberController::SequenceOpenButtonClicked()
+{
+ m_SequencePreviewRenderer->Stop();
+ auto filenames = m_Fractorium->SetupOpenXmlDialog();
+
+ if (!filenames.empty())
+ {
+ size_t i;
+ EmberFile emberFile;
+ XmlToEmber parser;
+ vector> embers;
+ vector 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(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); }
+
+///
+/// Constrain all min/max spinboxes.
+///
+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); }
+
+///
+/// Save all sequence settings to match the values in the controls.
+///
+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;
diff --git a/Source/Fractorium/FractoriumMenus.cpp b/Source/Fractorium/FractoriumMenus.cpp
index dcfef8f..ffee822 100644
--- a/Source/Fractorium/FractoriumMenus.cpp
+++ b/Source/Fractorium/FractoriumMenus.cpp
@@ -52,7 +52,7 @@ template
void FractoriumEmberController::NewFlock(size_t count)
{
Ember ember;
- StopPreviewRender();
+ StopAllPreviewRenderers();
m_EmberFile.Clear();
m_EmberFile.m_Filename = EmberFile::DefaultFilename();
@@ -91,7 +91,7 @@ void FractoriumEmberController::NewEmptyFlameInCurrentFile()
Ember ember;
Xform xform;
QDateTime local(QDateTime::currentDateTime());
- StopPreviewRender();
+ StopAllPreviewRenderers();
ParamsToEmber(ember);
xform.m_Weight = T(0.25);
xform.m_ColorX = m_Rand.Frand01();
@@ -116,7 +116,7 @@ template
void FractoriumEmberController::NewRandomFlameInCurrentFile()
{
Ember ember;
- StopPreviewRender();
+ StopAllPreviewRenderers();
m_SheepTools->Random(ember, m_FilteredVariations, static_cast