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(QTIsaac::LockedFrand(-2, 2)), 0, MAX_CL_VARS); ParamsToEmber(ember); ember.m_Name = EmberFile::DefaultEmberName(m_EmberFile.Size() + 1).toStdString(); @@ -138,7 +138,7 @@ template void FractoriumEmberController::CopyFlameInCurrentFile() { auto ember = m_Ember; - StopPreviewRender(); + StopAllPreviewRenderers(); ember.m_Name = EmberFile::DefaultEmberName(m_EmberFile.Size() + 1).toStdString(); ember.m_Index = m_EmberFile.Size(); m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync. @@ -172,7 +172,7 @@ void FractoriumEmberController::OpenAndPrepFiles(const QStringList& filenames vector> embers; vector errors; uint previousSize = append ? uint(m_EmberFile.Size()) : 0u; - StopPreviewRender(); + StopAllPreviewRenderers(); emberFile.m_Filename = filenames[0]; for (auto& filename : filenames) @@ -377,7 +377,7 @@ uint FractoriumEmberController::SaveCurrentToOpenedFile(bool render) uint i = 0; bool fileFound = false; - if (!m_PreviewRunning) + if (!m_LibraryPreviewRenderer->Running()) { for (auto& it : m_EmberFile.m_Embers) { @@ -393,7 +393,7 @@ uint FractoriumEmberController::SaveCurrentToOpenedFile(bool render) if (!fileFound) { - StopPreviewRender(); + StopAllPreviewRenderers(); m_EmberFile.m_Embers.push_back(m_Ember); m_EmberFile.MakeNamesUnique(); @@ -401,7 +401,7 @@ uint FractoriumEmberController::SaveCurrentToOpenedFile(bool render) UpdateLibraryTree(); } else if (render) - RenderPreviews(i, i + 1); + RenderLibraryPreviews(i, i + 1); } return i; @@ -532,7 +532,7 @@ void FractoriumEmberController::PasteXmlAppend() } b.clear(); - StopPreviewRender(); + StopAllPreviewRenderers(); parser.Parse(reinterpret_cast(const_cast(s.c_str())), "", embers); errors = parser.ErrorReportString(); @@ -586,7 +586,7 @@ void FractoriumEmberController::PasteXmlOver() } b.clear(); - StopPreviewRender(); + StopAllPreviewRenderers(); m_EmberFile.m_Embers.clear();//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync. parser.Parse(reinterpret_cast(const_cast(s.c_str())), "", m_EmberFile.m_Embers); errors = parser.ErrorReportString(); @@ -812,14 +812,14 @@ void Fractorium::OnActionClearFlame(bool checked) { m_Controller->ClearFlame(); /// void Fractorium::OnActionRenderPreviews(bool checked) { - m_Controller->RenderPreviews(); + m_Controller->RenderLibraryPreviews(); } /// /// Stop all previews from being rendered. This is handy if the user /// opens a large file with many embers in it, such as an animation sequence. /// -void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopPreviewRender(); } +void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopLibraryPreviewRender(); } /// /// Show the final render dialog as a modeless dialog to allow @@ -832,10 +832,10 @@ void Fractorium::OnActionFinalRender(bool checked) { //First completely stop what the current rendering process is doing. m_Controller->DeleteRenderer();//Delete the renderer, but not the controller. - m_Controller->StopPreviewRender(); + m_Controller->StopAllPreviewRenderers(); m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file. m_RenderStatusLabel->setText("Renderer stopped."); - m_FinalRenderDialog->show(); + m_FinalRenderDialog->Show(false); } /// diff --git a/Source/Fractorium/FractoriumParams.cpp b/Source/Fractorium/FractoriumParams.cpp index e17a924..3be193a 100644 --- a/Source/Fractorium/FractoriumParams.cpp +++ b/Source/Fractorium/FractoriumParams.cpp @@ -24,7 +24,7 @@ void Fractorium::InitParamsUI() //Color. SetupSpinner(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.05, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0); SetupSpinner(table, this, row, 1, m_GammaSpin, spinHeight, 1, 9999, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0); - SetupSpinner(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0); + SetupSpinner(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0); SetupSpinner(table, this, row, 1, m_VibrancySpin, spinHeight, 0, 30, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 0.0); SetupSpinner(table, this, row, 1, m_HighlightSpin, spinHeight, 1.0, 10, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHighlightPowerChanged(double)), true, -1.0, -1.0, -1.0); m_GammaThresholdSpin->setDecimals(4); @@ -469,19 +469,17 @@ void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& tex template void FractoriumEmberController::DEFilterMinRadiusWidthChanged(double d) { - if (m_Fractorium->m_DEFilterMinRadiusSpin->value() > m_Fractorium->m_DEFilterMaxRadiusSpin->value()) - { - m_Fractorium->m_DEFilterMinRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMaxRadiusSpin->value()); - return; - } - UpdateAll([&](Ember& ember) { ember.m_MinRadDE = d; }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); } -void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFilterMinRadiusWidthChanged(d); } +void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) +{ + if (!ConstrainLow(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin)) + m_Controller->DEFilterMinRadiusWidthChanged(d); +} /// /// Set the density estimation filter max radius value. @@ -491,19 +489,17 @@ void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFil template void FractoriumEmberController::DEFilterMaxRadiusWidthChanged(double d) { - if (m_Fractorium->m_DEFilterMaxRadiusSpin->value() < m_Fractorium->m_DEFilterMinRadiusSpin->value()) - { - m_Fractorium->m_DEFilterMaxRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMinRadiusSpin->value()); - return; - } - UpdateAll([&](Ember& ember) { ember.m_MaxRadDE = d; }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); } -void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d) { m_Controller->DEFilterMaxRadiusWidthChanged(d); } +void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d) +{ + if (!ConstrainHigh(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin)) + m_Controller->DEFilterMaxRadiusWidthChanged(d); +} /// /// Set the density estimation filter curve value. diff --git a/Source/Fractorium/FractoriumPch.h b/Source/Fractorium/FractoriumPch.h index 894af86..0d410dd 100644 --- a/Source/Fractorium/FractoriumPch.h +++ b/Source/Fractorium/FractoriumPch.h @@ -85,4 +85,6 @@ using namespace std; using namespace EmberNs; using namespace EmberCLns; +using namespace EmberCommon; + #endif diff --git a/Source/Fractorium/FractoriumRender.cpp b/Source/Fractorium/FractoriumRender.cpp index 7026111..a014879 100644 --- a/Source/Fractorium/FractoriumRender.cpp +++ b/Source/Fractorium/FractoriumRender.cpp @@ -580,13 +580,11 @@ bool FractoriumEmberController::CreateRenderer(eRendererType renderType, cons else m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG); - if ((m_Renderer->EarlyClip() != m_PreviewRenderer->EarlyClip()) || - (m_Renderer->YAxisUp() != m_PreviewRenderer->YAxisUp())) + if ((m_Renderer->EarlyClip() != m_LibraryPreviewRenderer->EarlyClip()) || + (m_Renderer->YAxisUp() != m_LibraryPreviewRenderer->YAxisUp()) || + (m_Renderer->Transparency() != m_LibraryPreviewRenderer->Transparency())) { - StopPreviewRender(); - m_PreviewRenderer->EarlyClip(m_Renderer->EarlyClip()); - m_PreviewRenderer->YAxisUp(m_Renderer->YAxisUp()); - RenderPreviews(); + RenderLibraryPreviews(); } m_FailedRenders = 0; @@ -690,7 +688,7 @@ bool Fractorium::CreateControllerFromOptions() if (m_Controller.get()) { scale = m_Controller->LockedScale(); - m_Controller->StopPreviewRender();//Must stop any previews first, else changing controllers will crash the program and SaveCurrentToOpenedFile() will return 0. + m_Controller->StopAllPreviewRenderers();//Must stop any previews first, else changing controllers will crash the program and SaveCurrentToOpenedFile() will return 0. current = m_Controller->SaveCurrentToOpenedFile(false); m_Controller->CopyTempPalette(tempPalette);//Convert float to double or save double verbatim; //Replace below with this once LLVM fixes a crash in their compiler with default lambda parameters.//TODO @@ -698,10 +696,10 @@ bool Fractorium::CreateControllerFromOptions() //m_Controller->CopyEmberFile(efd); #ifdef DO_DOUBLE m_Controller->CopyEmber(ed, [&](Ember& ember) { }); - m_Controller->CopyEmberFile(efd, [&](Ember& ember) { }); + m_Controller->CopyEmberFile(efd, false, [&](Ember& ember) { }); #else m_Controller->CopyEmber(ed, [&](Ember& ember) { }); - m_Controller->CopyEmberFile(efd, [&](Ember& ember) { }); + m_Controller->CopyEmberFile(efd, false, [&](Ember& ember) { }); #endif m_Controller->Shutdown(); } @@ -718,7 +716,7 @@ bool Fractorium::CreateControllerFromOptions() if (m_Controller.get()) { ed.m_Palette = tempPalette;//Restore base temp palette. Adjustments will be then be applied and stored back in in m_Ember.m_Palette below. - m_Controller->SetEmberFile(efd); + m_Controller->SetEmberFile(efd, true); m_Controller->SetEmber(current, true); m_Controller->LockedScale(scale); //Setting these and updating the GUI overwrites the work of clearing them done in SetEmber() above. diff --git a/Source/Fractorium/FractoriumSettings.cpp b/Source/Fractorium/FractoriumSettings.cpp index 1d42aeb..8cb5d09 100644 --- a/Source/Fractorium/FractoriumSettings.cpp +++ b/Source/Fractorium/FractoriumSettings.cpp @@ -134,6 +134,51 @@ void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH, uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); } void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); } +/// +/// Sequence generation settings. +/// + +uint FractoriumSettings::FramesPerRot() { return value(FRAMESPERROT).toUInt(); } +void FractoriumSettings::FramesPerRot(uint i) { setValue(FRAMESPERROT, i); } +uint FractoriumSettings::FramesPerRotMax() { return value(FRAMESPERROTMAX).toUInt(); } +void FractoriumSettings::FramesPerRotMax(uint i) { setValue(FRAMESPERROTMAX, i); } +uint FractoriumSettings::Rotations() { return value(ROTATIONS).toUInt(); } +void FractoriumSettings::Rotations(uint i) { setValue(ROTATIONS, i); } +uint FractoriumSettings::RotationsMax() { return value(ROTATIONSMAX).toUInt(); } +void FractoriumSettings::RotationsMax(uint i) { setValue(ROTATIONSMAX, i); } +uint FractoriumSettings::BlendFrames() { return value(BLENDFRAMES).toUInt(); } +void FractoriumSettings::BlendFrames(uint i) { setValue(BLENDFRAMES, i); } +uint FractoriumSettings::BlendFramesMax() { return value(BLENDFRAMESMAX).toUInt(); } +void FractoriumSettings::BlendFramesMax(uint i) { setValue(BLENDFRAMESMAX, i); } + +/// +/// Variations filter settings. +/// + +int FractoriumSettings::VarFilterSum() { return value(VARFILTERSUM).toInt(); } +void FractoriumSettings::VarFilterSum(int i) { setValue(VARFILTERSUM, i); } + +int FractoriumSettings::VarFilterAssign() { return value(VARFILTERASSIGN).toInt(); } +void FractoriumSettings::VarFilterAssign(int i) { setValue(VARFILTERASSIGN, i); } + +int FractoriumSettings::VarFilterPpsum() { return value(VARFILTERPPSUM).toInt(); } +void FractoriumSettings::VarFilterPpsum(int i) { setValue(VARFILTERPPSUM, i); } + +int FractoriumSettings::VarFilterPpassign() { return value(VARFILTERPPASSIGN).toInt(); } +void FractoriumSettings::VarFilterPpassign(int i) { setValue(VARFILTERPPASSIGN, i); } + +int FractoriumSettings::VarFilterSdc() { return value(VARFILTERSDC).toInt(); } +void FractoriumSettings::VarFilterSdc(int i) { setValue(VARFILTERSDC, i); } + +int FractoriumSettings::VarFilterState() { return value(VARFILTERSTATE).toInt(); } +void FractoriumSettings::VarFilterState(int i) { setValue(VARFILTERSTATE, i); } + +int FractoriumSettings::VarFilterParam() { return value(VARFILTERPARAM).toInt(); } +void FractoriumSettings::VarFilterParam(int i) { setValue(VARFILTERPARAM, i); } + +int FractoriumSettings::VarFilterNonparam() { return value(VARFILTERNONPARAM).toInt(); } +void FractoriumSettings::VarFilterNonparam(int i) { setValue(VARFILTERNONPARAM, i); } + /// /// Final render settings. /// diff --git a/Source/Fractorium/FractoriumSettings.h b/Source/Fractorium/FractoriumSettings.h index 3f1b337..c0f3ffe 100644 --- a/Source/Fractorium/FractoriumSettings.h +++ b/Source/Fractorium/FractoriumSettings.h @@ -21,12 +21,19 @@ #define OPENCLSUBBATCH "render/openclsubbatch" #define RANDOMCOUNT "render/randomcount" +#define FRAMESPERROT "sequence/framesperrot" +#define FRAMESPERROTMAX "sequence/framesperrotmax" +#define ROTATIONS "sequence/rotations" +#define ROTATIONSMAX "sequence/rotationsmax" +#define BLENDFRAMES "sequence/blendframes" +#define BLENDFRAMESMAX "sequence/blendframesmax" + #define VARFILTERSUM "varfilter/sumcheckbox" #define VARFILTERASSIGN "varfilter/assigncheckbox" #define VARFILTERPPSUM "varfilter/ppsumcheckbox" #define VARFILTERPPASSIGN "varfilter/ppassigncheckbox" #define VARFILTERSDC "varfilter/dccheckbox" -#define VARFILTERSSTATE "varfilter/statecheckbox" +#define VARFILTERSTATE "varfilter/statecheckbox" #define VARFILTERPARAM "varfilter/paramcheckbox" #define VARFILTERNONPARAM "varfilter/nonparamcheckbox" @@ -126,6 +133,43 @@ public: uint RandomCount(); void RandomCount(uint i); + uint FramesPerRot(); + void FramesPerRot(uint i); + uint FramesPerRotMax(); + void FramesPerRotMax(uint i); + uint Rotations(); + void Rotations(uint i); + uint RotationsMax(); + void RotationsMax(uint i); + uint BlendFrames(); + void BlendFrames(uint i); + uint BlendFramesMax(); + void BlendFramesMax(uint i); + + int VarFilterSum(); + void VarFilterSum(int i); + + int VarFilterAssign(); + void VarFilterAssign(int i); + + int VarFilterPpsum(); + void VarFilterPpsum(int i); + + int VarFilterPpassign(); + void VarFilterPpassign(int i); + + int VarFilterSdc(); + void VarFilterSdc(int i); + + int VarFilterState(); + void VarFilterState(int i); + + int VarFilterParam(); + void VarFilterParam(int i); + + int VarFilterNonparam(); + void VarFilterNonparam(int i); + bool FinalEarlyClip(); void FinalEarlyClip(bool b); diff --git a/Source/Fractorium/VariationsDialog.cpp b/Source/Fractorium/VariationsDialog.cpp index 024e158..99c2b17 100644 --- a/Source/Fractorium/VariationsDialog.cpp +++ b/Source/Fractorium/VariationsDialog.cpp @@ -26,14 +26,14 @@ FractoriumVariationsDialog::FractoriumVariationsDialog(FractoriumSettings* setti m_CheckBoxes.push_back(ui.StateCheckBox); m_CheckBoxes.push_back(ui.ParamCheckBox); m_CheckBoxes.push_back(ui.NonParamCheckBox); - ui.SumCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSUM).toInt())); - ui.AssignCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERASSIGN).toInt())); - ui.PpSumCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPPSUM).toInt())); - ui.PpAssignCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPPASSIGN).toInt())); - ui.DcCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSDC).toInt())); - ui.StateCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSSTATE).toInt())); - ui.ParamCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPARAM).toInt())); - ui.NonParamCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERNONPARAM).toInt())); + ui.SumCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterSum ())); + ui.AssignCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterAssign ())); + ui.PpSumCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterPpsum ())); + ui.PpAssignCheckBox->setCheckState(Qt::CheckState(m_Settings->VarFilterPpassign())); + ui.DcCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterSdc ())); + ui.StateCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterState ())); + ui.ParamCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterParam ())); + ui.NonParamCheckBox->setCheckState(Qt::CheckState(m_Settings->VarFilterNonparam())); for (auto& cb : m_CheckBoxes) { @@ -112,14 +112,14 @@ void FractoriumVariationsDialog::SyncSettings() m[cb->text()] = cb->checkState() == Qt::CheckState::Checked; }); m_Settings->Variations(m); - m_Settings->setValue(VARFILTERSUM , int(ui.SumCheckBox->checkState())); - m_Settings->setValue(VARFILTERASSIGN , int(ui.AssignCheckBox->checkState())); - m_Settings->setValue(VARFILTERPPSUM , int(ui.PpSumCheckBox->checkState())); - m_Settings->setValue(VARFILTERPPASSIGN, int(ui.PpAssignCheckBox->checkState())); - m_Settings->setValue(VARFILTERSDC , int(ui.DcCheckBox->checkState())); - m_Settings->setValue(VARFILTERSSTATE , int(ui.StateCheckBox->checkState())); - m_Settings->setValue(VARFILTERPARAM , int(ui.ParamCheckBox->checkState())); - m_Settings->setValue(VARFILTERNONPARAM, int(ui.NonParamCheckBox->checkState())); + m_Settings->VarFilterSum (int(ui.SumCheckBox->checkState())); + m_Settings->VarFilterAssign (int(ui.AssignCheckBox->checkState())); + m_Settings->VarFilterPpsum (int(ui.PpSumCheckBox->checkState())); + m_Settings->VarFilterPpassign(int(ui.PpAssignCheckBox->checkState())); + m_Settings->VarFilterSdc (int(ui.DcCheckBox->checkState())); + m_Settings->VarFilterState (int(ui.StateCheckBox->checkState())); + m_Settings->VarFilterParam (int(ui.ParamCheckBox->checkState())); + m_Settings->VarFilterNonparam(int(ui.NonParamCheckBox->checkState())); } /// diff --git a/Source/Fractorium/VariationsDialog.ui b/Source/Fractorium/VariationsDialog.ui index 254a354..1ddf399 100644 --- a/Source/Fractorium/VariationsDialog.ui +++ b/Source/Fractorium/VariationsDialog.ui @@ -243,6 +243,20 @@ + + SelectAllButton + InvertSelectionButton + SelectNoneButton + SumCheckBox + AssignCheckBox + PpSumCheckBox + PpAssignCheckBox + DcCheckBox + StateCheckBox + ParamCheckBox + NonParamCheckBox + VariationsTable +