fractorium/Source/EmberGenome/EmberGenome.cpp
Person a0a205edd8 --User changes
-Users can now specify animation params on a per flame basis.
 --These get saved with the flame file.
 -Allow for rotating xforms around the world origin during animation.
 -Make the Clear Flame menu item be more comprehensive in how it clears a flame out.

--Bug fixes
 -Fix an extremely rare possible memory leak when using motion during animation, which is never used in Fractorium.
 -Do not skip to the current flame index, or attach a prefix in the Final Render Dialog when rendering an animation sequence.

--Code changes
 -Place all animation params in Ember.
2024-03-16 10:15:51 -06:00

889 lines
27 KiB
C++

#include "EmberCommonPch.h"
#include "EmberGenome.h"
#include "JpegUtils.h"
#include <xmmintrin.h>
#include <immintrin.h>
#include <pmmintrin.h>
using namespace EmberCommon;
/// <summary>
/// Set various default test values on the passed in ember.
/// </summary>
/// <param name="ember">The ember to test</param>
template <typename T>
void SetDefaultTestValues(Ember<T>& ember)
{
ember.m_Time = 0.0;
ember.m_Interp = eInterp::EMBER_INTERP_LINEAR;
ember.m_PaletteInterp = ePaletteInterp::INTERP_HSV;
ember.m_Background[0] = 0;
ember.m_Background[1] = 0;
ember.m_Background[2] = 0;
ember.m_Background[3] = 255;
ember.m_CenterX = 0;
ember.m_CenterY = 0;
ember.m_Rotate = 0;
ember.m_PixelsPerUnit = 64;
ember.m_FinalRasW = 128;
ember.m_FinalRasH = 128;
ember.m_Supersample = 1;
ember.m_SpatialFilterRadius = T(0.5);
ember.m_SpatialFilterType = eSpatialFilterType::GAUSSIAN_SPATIAL_FILTER;
ember.m_Zoom = 0;
ember.m_Quality = 1;
ember.m_TemporalSamples = 1;
ember.m_MaxRadDE = 0;
ember.m_MinRadDE = 0;
ember.m_CurveDE = T(0.6);
}
/// <summary>
/// The core of the EmberGenome.exe program.
/// Template argument expected to be float or double.
/// </summary>
/// <param name="opt">A populated EmberOptions object which specifies all program options to be used</param>
/// <returns>True if success, else false.</returns>
template <typename T>
bool EmberGenome(int argc, _TCHAR* argv[], EmberOptions& opt)
{
auto info = OpenCLInfo::Instance();
std::cout.imbue(std::locale(""));
if (opt.DumpArgs())
cerr << opt.GetValues(eOptionUse::OPT_USE_GENOME) << "\n";
if (opt.OpenCLInfo())
{
cerr << "\nOpenCL Info: \n";
cerr << info->DumpInfo();
return true;
}
Compat::m_Compat = opt.Flam3Compat();
auto varList = VariationList<T>::Instance();
if (opt.AllVars() || opt.SumVars() || opt.AssignVars() || opt.PpSumVars() || opt.PpAssignVars() ||
opt.DcVars() || opt.StateVars() || opt.ParVars() || opt.NonParVars() ||
opt.RegVars() || opt.PreVars() || opt.PostVars())
{
vector<string> assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =",
"outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" };
if (opt.AllVars())
{
auto& vars = varList->AllVars();
for (auto& v : vars)
cout << v->Name() << "\n";
}
else if (opt.SumVars())
{
auto& reg = varList->RegVars();
auto matches = FindVarsWithout<T>(varList->RegVars(), assign);
for (auto& v : matches)
cout << v->Name() << "\n";
}
else if (opt.AssignVars())
{
auto matches = FindVarsWith<T>(varList->RegVars(), assign);
for (auto& v : matches)
cout << v->Name() << "\n";
}
else if (opt.PpSumVars())
{
auto& pre = varList->PreVars();
auto& post = varList->PostVars();
for (auto& v : pre)
if (v->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
cout << v->Name() << "\n";
for (auto& v : post)
if (v->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
cout << v->Name() << "\n";
}
else if (opt.PpAssignVars())
{
auto& pre = varList->PreVars();
auto& post = varList->PostVars();
for (auto& v : pre)
if (v->AssignType() == eVariationAssignType::ASSIGNTYPE_SET)
cout << v->Name() << "\n";
for (auto& v : post)
if (v->AssignType() == eVariationAssignType::ASSIGNTYPE_SET)
cout << v->Name() << "\n";
}
else if (opt.DcVars())
{
auto& all = varList->AllVars();
auto matches = FindVarsWith<T>(all, vector<string> { "m_ColorX" });
for (auto& v : matches)
cout << v->Name() << "\n";
}
else if (opt.StateVars())
{
auto& all = varList->AllVars();
for (auto& v : all)
if (!v->StateOpenCLString().empty())
cout << v->Name() << "\n";
}
else if (opt.ParVars())
{
auto& parVars = varList->ParametricVariations();
for (auto& v : parVars)
cout << v->Name() << "\n";
}
else if (opt.NonParVars())
{
auto& vars = varList->NonParametricVariations();
for (auto& v : vars)
cout << v->Name() << "\n";
}
else
{
vector<const Variation<T>*> vars;
if (opt.RegVars())
vars.insert(vars.end(), varList->RegVars().begin(), varList->RegVars().end());
if (opt.PreVars())
vars.insert(vars.end(), varList->PreVars().begin(), varList->PreVars().end());
if (opt.PostVars())
vars.insert(vars.end(), varList->PostVars().begin(), varList->PostVars().end());
for (auto& v : vars)
cout << v->Name() << "\n";
}
return true;
}
VerbosePrint("Using " << (sizeof(T) == sizeof(float) ? "single" : "double") << " precision.");
//Regular variables.
Timing t;
bool exactTimeMatch, randomMode, didColor, seqFlag, random = false;
size_t i, i0, i1, rep, val, frame, frameCount, count = 0;
size_t ftime, firstFrame, lastFrame;
double tot;
size_t n, totb, totw;
double avgPix;
T fractionBlack, fractionWhite, blend, spread, mix0, mix1;
string token, filename;
ostringstream os, os2;
vector<Ember<T>> embers, embers2, templateEmbers;
vector<eVariationId> vars, noVars;
vector<v4F> finalImage;
eCrossMode crossMeth;
eMutateMode mutMeth;
Ember<T> orig, save, selp0, selp1, parent0, parent1;
Ember<T>* aselp0, *aselp1, *pTemplate = nullptr;
XmlToEmber<T> parser;
EmberToXml<T> emberToXml;
Interpolater<T> interpolater;
EmberReport emberReport, emberReport2;
const vector<pair<size_t, size_t>> devices = Devices(opt.Devices());
auto progress = make_unique<RenderProgress<T>>();
unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, devices, false, 0, emberReport));
QTIsaac<ISAAC_SIZE, ISAAC_INT> rand(ISAAC_INT(t.Tic()), ISAAC_INT(t.Tic() * 2), ISAAC_INT(t.Tic() * 3));
vector<string> errorReport = emberReport.ErrorReport();
auto fullpath = GetExePath(argv[0]);
os.imbue(std::locale(""));
os2.imbue(std::locale(""));
if (!errorReport.empty())
cerr << emberReport.ErrorReportString();
if (!renderer.get())
{
cerr << "Renderer creation failed, exiting.\n";
return false;
}
if (!InitPaletteList<float>(fullpath, opt.PalettePath()))
return false;
if (!opt.EmberCL())
{
if (opt.ThreadCount() != 0)
renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
}
else
{
cerr << "Using OpenCL to render.\n";
if (opt.Verbose())
{
for (auto& device : devices)
{
cerr << "Platform: " << info->PlatformName(device.first) << "\n";
cerr << "Device: " << info->DeviceName(device.first, device.second) << "\n";
}
}
}
//SheepTools will own the created renderer and will take care of cleaning it up.
SheepTools<T, float> tools(opt.PalettePath(), CreateRenderer<T>(opt.EmberCL() ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, devices, false, 0, emberReport2));
tools.SetSpinParams(!opt.UnsmoothEdge(),
T(opt.Stagger()),
T(opt.OffsetX()),
T(opt.OffsetY()),
opt.Nick(),
opt.Url(),
opt.Id(),
opt.Comment(),
opt.SheepGen(),
opt.SheepId());
if (opt.UseVars() != "" && opt.DontUseVars() != "")
{
cerr << "use_vars and dont_use_vars cannot both be specified. Returning without executing.\n";
return false;
}
//Specify reasonable defaults if nothing is specified.
if (opt.UseVars() == "" && opt.DontUseVars() == "")
{
noVars.push_back(eVariationId::VAR_NOISE);
noVars.push_back(eVariationId::VAR_BLUR);
noVars.push_back(eVariationId::VAR_GAUSSIAN_BLUR);
noVars.push_back(eVariationId::VAR_RADIAL_BLUR);
noVars.push_back(eVariationId::VAR_NGON);
noVars.push_back(eVariationId::VAR_SQUARE);
noVars.push_back(eVariationId::VAR_RAYS);
noVars.push_back(eVariationId::VAR_CROSS);
noVars.push_back(eVariationId::VAR_PRE_BLUR);
noVars.push_back(eVariationId::VAR_SEPARATION);
noVars.push_back(eVariationId::VAR_SPLIT);
noVars.push_back(eVariationId::VAR_SPLITS);
//Set ivars to the complement of novars.
for (i = 0; i < varList->Size(); i++)
if (!Contains(noVars, varList->GetVariation(i)->VariationId()))
vars.push_back(varList->GetVariation(i)->VariationId());
}
else
{
if (opt.UseVars() != "")//Parse comma-separated list of variations to use.
{
istringstream iss(opt.UseVars());
while (std::getline(iss, token, ','))
{
if (parser.Aton(token.c_str(), val))
{
if (val < varList->Size())
vars.push_back(static_cast<eVariationId>(val));
}
}
}
else if (opt.DontUseVars() != "")
{
istringstream iss(opt.DontUseVars());
while (std::getline(iss, token, ','))
{
if (parser.Aton(token.c_str(), val))
{
if (val < varList->Size())
noVars.push_back(static_cast<eVariationId>(val));
}
}
//Set ivars to the complement of novars.
for (i = 0; i < varList->Size(); i++)
if (!Contains(noVars, varList->GetVariation(i)->VariationId()))
vars.push_back(varList->GetVariation(i)->VariationId());
}
}
const auto doMutate = opt.Mutate() != "";
const auto doInter = opt.Inter() != "";
const auto doRotate = opt.Rotate() != "";
const auto doClone = opt.Clone() != "";
const auto doCross0 = opt.Cross0() != "";
const auto doCross1 = opt.Cross1() != "";
count += (doMutate ? 1 : 0);
count += (doInter ? 1 : 0);
count += (doRotate ? 1 : 0);
count += (doClone ? 1 : 0);
count += ((doCross0 || doCross1) ? 1 : 0);
if (count > 1)
{
cerr << "Can only specify one of mutate, clone, cross, rotate, or inter. Returning without executing.\n";
return false;
}
if (doCross0 != doCross1)//Must both be either true or false.
{
cerr << "Must specify both crossover arguments. Returning without executing.\n";
return false;
}
if (opt.Method() != "" && (!doCross0 && !doMutate))
{
cerr << "Cannot specify method unless doing crossover or mutate. Returning without executing.\n";
return false;
}
if (opt.TemplateFile() != "")
{
if (!ParseEmberFile(parser, opt.TemplateFile(), templateEmbers, false))//Do not use defaults here to ensure only present fields get used when applying the template.
return false;
if (templateEmbers.size() > 1)
cerr << "More than one control point in template, ignoring all but first.\n";
pTemplate = &templateEmbers[0];
}
//Methods for genetic manipulation begin here.
if (doMutate) filename = opt.Mutate();
else if (doInter) filename = opt.Inter();
else if (doRotate) filename = opt.Rotate();
else if (doClone) filename = opt.Clone();
else if (doCross0) filename = opt.Cross0();
else if (opt.CloneAll() != "") filename = opt.CloneAll();
else if (opt.Animate() != "") filename = opt.Animate();
else if (opt.Sequence() != "") filename = opt.Sequence();
else if (opt.Inter() != "") filename = opt.Inter();
else if (opt.Rotate() != "") filename = opt.Rotate();
else if (opt.Clone() != "") filename = opt.Clone();
else if (opt.Mutate() != "") filename = opt.Mutate();
else random = true;
if (!random)
{
if (!ParseEmberFile(parser, filename, embers))
return false;
if (doCross1)
{
if (!ParseEmberFile(parser, opt.Cross1(), embers2))
return false;
}
}
if (opt.CloneAll() != "")
{
cout << "<clone_all version=\"Ember-" << EmberVersion() << "\">\n";
for (i = 0; i < embers.size(); i++)
{
if (pTemplate)
tools.ApplyTemplate(embers[i], *pTemplate);
tools.Offset(embers[i], T(opt.OffsetX()), T(opt.OffsetY()));
cout << emberToXml.ToString(embers[i], opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
cout << "</clone_all>\n";
return true;
}
if (opt.Animate() != "")
{
Ember<T> interpolated;
for (i = 0; i < embers.size(); i++)
{
if (i > 0 && embers[i].m_Time <= embers[i - 1].m_Time)
{
cerr << "Error: control points must be sorted by time, but time " << embers[i].m_Time << " <= " << embers[i - 1].m_Time << ", index " << i << ".\n";
return false;
}
embers[i].DeleteMotionElements();
}
firstFrame = size_t(opt.FirstFrame() == UINT_MAX ? embers[0].m_Time : opt.FirstFrame());
lastFrame = size_t(opt.LastFrame() == UINT_MAX ? embers.back().m_Time : opt.LastFrame());
if (lastFrame < firstFrame)
lastFrame = firstFrame;
cout << "<animate version=\"EMBER-" << EmberVersion() << "\">\n";
for (ftime = firstFrame; ftime <= lastFrame; ftime++)
{
exactTimeMatch = false;
for (i = 0; i < embers.size(); i++)
{
if (ftime == size_t(embers[i].m_Time))
{
interpolated = embers[i];
exactTimeMatch = true;
break;
}
}
if (!exactTimeMatch)
{
interpolater.Interpolate(embers, T(ftime), T(opt.Stagger()), interpolated);
for (i = 0; i < embers.size(); i++)
{
if (ftime == size_t(embers[i].m_Time - 1))
{
exactTimeMatch = true;
break;
}
}
if (!exactTimeMatch)
interpolated.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LINEAR;
}
if (pTemplate)
tools.ApplyTemplate(interpolated, *pTemplate);
cout << emberToXml.ToString(interpolated, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
cout << "</animate>\n";
return true;
}
//Note that in Fractorium, the sequence is created from the params stored in the flame file.
//Here, it's done using the values passed on the command line, which will override the values in the file.
if (opt.Sequence() != "")
{
Ember<T> result;
if (!opt.LoopFrames() && !opt.InterpFrames())
{
cerr << "loop frames or interp frames must be positive and non-zero, not " << opt.LoopFrames() << ", " << opt.InterpFrames() << ".\n";
return false;
}
if (opt.LoopFrames() > 0 && !opt.Loops())
{
cerr << "loop frames cannot be positive while loops is zero: " << opt.LoopFrames() << ", " << opt.Loops() << ".\n";
return false;
}
if (opt.Loops() > 0 && !opt.LoopFrames())
{
cerr << "loops cannot be positive while loopframes is zero: " << opt.Loops() << ", " << opt.LoopFrames() << ".\n";
return false;
}
if (opt.Enclosed())
cout << "<sequence version=\"EMBER-" << EmberVersion() << "\">\n";
frameCount = 0;
os.str("");
os << setfill('0') << setprecision(0) << fixed;
const auto padding = opt.Padding() ? streamsize(opt.Padding()) : (streamsize(std::log10(opt.StartCount() + (((opt.LoopFrames() * opt.Loops()) + opt.InterpFrames()) * embers.size()))) + 1);
t.Tic();
for (i = 0; i < embers.size(); i++)
{
if (opt.Loops() > 0)
{
const auto roundFrames = static_cast<size_t>(std::round(opt.LoopFrames() * opt.Loops()));
for (frame = 0; frame < roundFrames; frame++)
{
blend = T(frame) / T(opt.LoopFrames());
tools.Spin(embers[i], pTemplate, result, opt.StartCount() + frameCount++, blend, opt.CwLoops());//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());
}
//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 = static_cast<T>(frame) / static_cast<T>(opt.LoopFrames());
tools.Spin(embers[i], pTemplate, result, opt.StartCount() + frameCount, blend, opt.CwLoops());//Do not increment frameCount here.
FormatName(result, os, padding);
}
if (i < embers.size() - 1)
{
if (opt.Loops() > 0)//Store the last result as the flame to interpolate from. This applies for whole or fractional values of opt.Loops().
embers[i] = result;
for (frame = 0; frame < opt.InterpFrames(); frame++)
{
seqFlag = frame == 0 || (frame == opt.InterpFrames() - 1);
blend = frame / static_cast<T>(opt.InterpFrames());
result.Clear();
tools.SpinInter(&embers[i], pTemplate, result, opt.StartCount() + frameCount++, seqFlag, blend, opt.InterpLoops(), opt.CwInterpLoops());
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
}
}
tools.Spin(embers.back(), pTemplate, result, opt.StartCount() + frameCount, 0, opt.CwInterpLoops());
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
t.Toc("Sequencing");
if (opt.Enclosed())
cout << "</sequence>\n";
return true;
}
if (doInter || doRotate)
{
Ember<T> result, result1, result2, result3;
if (!opt.LoopFrames() && !opt.InterpFrames())
{
cerr << "loop frames or interp frames must be positive and non-zero, not " << opt.LoopFrames() << ", " << opt.InterpFrames() << ".\n";
return false;
}
frame = opt.Frame();
blend = frame / static_cast<T>(opt.InterpFrames());//Percentage between first and second flame to treat as the center flame.
spread = 1 / static_cast<T>(opt.InterpFrames());//Amount to move backward and forward from the center flame.
if (opt.Enclosed())
cout << "<pick version=\"EMBER-" << EmberVersion() << "\">\n";
if (doRotate)
{
if (embers.size() != 1)
{
cerr << "rotation requires one control point, not " << embers.size() << ".\n";
return false;
}
if (frame)//Cannot spin backward below frame zero.
{
tools.Spin(embers[0], pTemplate, result1, frame - 1, blend - spread, opt.CwLoops());
cout << emberToXml.ToString(result1, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
tools.Spin(embers[0], pTemplate, result2, frame, blend, opt.CwLoops());
tools.Spin(embers[0], pTemplate, result3, frame + 1, blend + spread, opt.CwLoops());
cout << emberToXml.ToString(result2, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
cout << emberToXml.ToString(result3, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
else
{
if (embers.size() != 2)
{
cerr << "interpolation requires two control points, not " << embers.size() << ".\n";
return false;
}
if (frame)//Cannot interpolate backward below frame zero.
{
tools.SpinInter(embers.data(), pTemplate, result1, frame - 1, false, blend - spread, opt.InterpLoops(), opt.CwInterpLoops());
cout << emberToXml.ToString(result1, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
tools.SpinInter(embers.data(), pTemplate, result2, frame, false, blend, opt.InterpLoops(), opt.CwInterpLoops());
tools.SpinInter(embers.data(), pTemplate, result3, frame + 1, false, blend + spread, opt.InterpLoops(), opt.CwInterpLoops());
cout << emberToXml.ToString(result2, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
cout << emberToXml.ToString(result3, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
if (opt.Enclosed())
cout << "</pick>\n";
return true;
}
//Repeat.
renderer->EarlyClip(opt.EarlyClip());
renderer->YAxisUp(opt.YAxisUp());
renderer->LockAccum(opt.LockAccum());
renderer->PixelAspectRatio(T(opt.AspectRatio()));
if (opt.Repeat() == 0)
{
cerr << "Repeat must be positive, not " << opt.Repeat() << "\n";
return false;
}
if (opt.Enclosed())
cout << "<pick version=\"EMBER-" << EmberVersion() << "\">\n";
for (rep = 0; rep < opt.Repeat(); rep++)
{
count = 0;
os.str("");
save.Clear();
VerbosePrint("Flame = " << rep + 1 << "/" << opt.Repeat() << "...");
if (opt.Clone() != "")
{
os << "clone";//Action is 'clone' with trunc vars concat.
if (opt.CloneAction() != "")
os << " " << opt.CloneAction();
selp0 = embers[rand.Rand(embers.size())];
save = selp0;
aselp0 = &selp0;
aselp1 = nullptr;
os << tools.TruncateVariations(save, 5);
save.m_Edits = emberToXml.CreateNewEditdoc(aselp0, aselp1, os.str(), opt.Nick(), opt.Url(), opt.Id(), opt.Comment(), opt.SheepGen(), opt.SheepId());
}
else
{
do
{
randomMode = false;
didColor = false;
os.str("");
VerbosePrint(".");
if (doMutate)
{
selp0 = embers[rand.Rand(embers.size())];
orig = selp0;
aselp0 = &selp0;
aselp1 = nullptr;
if (opt.Method() == "")
mutMeth = eMutateMode::MUTATE_NOT_SPECIFIED;
else if (opt.Method() == "all_vars")
mutMeth = eMutateMode::MUTATE_ALL_VARIATIONS;
else if (opt.Method() == "one_xform")
mutMeth = eMutateMode::MUTATE_ONE_XFORM_COEFS;
else if (opt.Method() == "add_symmetry")
mutMeth = eMutateMode::MUTATE_ADD_SYMMETRY;
else if (opt.Method() == "post_xforms")
mutMeth = eMutateMode::MUTATE_POST_XFORMS;
else if (opt.Method() == "color_palette")
mutMeth = eMutateMode::MUTATE_COLOR_PALETTE;
else if (opt.Method() == "delete_xform")
mutMeth = eMutateMode::MUTATE_DELETE_XFORM;
else if (opt.Method() == "all_coefs")
mutMeth = eMutateMode::MUTATE_ALL_COEFS;
else
{
cerr << "method " << opt.Method() << " not defined for mutate. Defaulting to random.\n";
mutMeth = eMutateMode::MUTATE_NOT_SPECIFIED;
}
os << tools.Mutate(orig, mutMeth, vars, opt.Symmetry(), T(opt.Speed()), 8);
//Scan string returned for 'mutate color'.
if (strstr(os.str().c_str(), "mutate color"))
didColor = true;
if (orig.m_Name != "")
{
os2.str("");
os2 << "mutation " << rep << " of " << orig.m_Name;
orig.m_Name = os2.str();
}
}
else if (doCross0)
{
i0 = rand.Rand(embers.size());
i1 = rand.Rand(embers2.size());
selp0 = embers[i0];
selp1 = embers2[i1];
aselp0 = &selp0;
aselp1 = &selp1;
if (opt.Method() == "")
crossMeth = eCrossMode::CROSS_NOT_SPECIFIED;
else if (opt.Method() == "union")
crossMeth = eCrossMode::CROSS_UNION;
else if (opt.Method() == "interpolate")
crossMeth = eCrossMode::CROSS_INTERPOLATE;
else if (opt.Method() == "alternate")
crossMeth = eCrossMode::CROSS_ALTERNATE;
else
{
cerr << "method '" << opt.Method() << "' not defined for cross. Defaulting to random.\n";
crossMeth = eCrossMode::CROSS_NOT_SPECIFIED;
}
os << tools.Cross(embers[i0], embers2[i1], orig, crossMeth);
if (embers[i0].m_Name != "" || embers2[i1].m_Name != "")
{
os2.str("");
os2 << rep << " of " << embers[i0].m_Name << " x " << embers2[i1].m_Name;
orig.m_Name = os2.str();
}
}
else
{
os << "random";
randomMode = true;
tools.Random(orig, vars, opt.Symmetry(), 0, 8);
orig.m_FinalRasW = 1920;
orig.m_FinalRasH = 1080;
aselp0 = nullptr;
aselp1 = nullptr;
}
//Adjust bounding box half the time.
if (rand.RandBit() || randomMode)
{
T bmin[2], bmax[2];
tools.EstimateBoundingBox(orig, T(0.01), 100000, bmin, bmax);
if (rand.Frand01<T>() < T(0.3))
{
orig.m_CenterX = (bmin[0] + bmax[0]) / 2;
orig.m_CenterY = (bmin[1] + bmax[1]) / 2;
os << " recentered";
}
else
{
if (rand.RandBit())
{
mix0 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
mix1 = rand.GoldenBit<T>();
os << " reframed0";
}
else if (rand.RandBit())
{
mix0 = rand.GoldenBit<T>();
mix1 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
os << " reframed1";
}
else
{
mix0 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
mix1 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
os << " reframed2";
}
orig.m_CenterX = mix0 * bmin[0] + (1 - mix0) * bmax[0];
orig.m_CenterY = mix1 * bmin[1] + (1 - mix1) * bmax[1];
}
orig.m_PixelsPerUnit = orig.m_FinalRasW / (bmax[0] - bmin[0]);
}
os << tools.TruncateVariations(orig, 5);
if (!didColor && rand.RandBit())
{
if (opt.Debug())
cerr << "improving colors...\n";
tools.ImproveColors(orig, 100, false, 10);
os << " improved colors";
}
orig.m_Edits = emberToXml.CreateNewEditdoc(aselp0, aselp1, os.str(), opt.Nick(), opt.Url(), opt.Id(), opt.Comment(), opt.SheepGen(), opt.SheepId());
save = orig;
SetDefaultTestValues(orig);
renderer->SetEmber(orig, eProcessAction::FULL_RENDER, true);
if (renderer->Run(finalImage) != eRenderStatus::RENDER_OK)
{
cerr << "Error: test image rendering failed, aborting.\n";
return false;
}
tot = 0;
totb = totw = 0;
n = orig.m_FinalRasW * orig.m_FinalRasH;
for (i = 0; i < n; i++)
{
tot += (finalImage[i].r + finalImage[i].g + finalImage[i].b);
if (0 == finalImage[i].r && 0 == finalImage[i].g && 0 == finalImage[i].b) totb++;
if (1 == finalImage[i].r && 1 == finalImage[i].g && 1 == finalImage[i].g) totw++;
}
avgPix = (tot / (3 * n));
fractionBlack = totb / T(n);
fractionWhite = totw / T(n);
if (opt.Debug())
cerr << "avgPix = " << avgPix << " fractionBlack = " << fractionBlack << " fractionWhite = " << fractionWhite << " n = " << n << "\n";
orig.Clear();
count++;
}
while ((avgPix < opt.AvgThresh() ||
fractionBlack < opt.BlackThresh() ||
fractionWhite > opt.WhiteLimit()) &&
count < opt.Tries());
if (count == opt.Tries())
cerr << "Warning: reached maximum attempts, giving up.\n";
}
if (pTemplate)
tools.ApplyTemplate(save, *pTemplate);
save.m_Time = T(rep);
if (opt.MaxXforms() != UINT_MAX)
{
save.m_Symmetry = 0;
while (save.TotalXformCount() > opt.MaxXforms())
save.DeleteTotalXform(save.TotalXformCount() - 1);
}
cout << emberToXml.ToString(save, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
VerbosePrint("\nDone. Action = " << os.str() << "\n");
cout.flush();
save.Clear();
}
if (opt.Enclosed())
cout << "</pick>\n";
return true;
}
/// <summary>
/// Main program entry point for EmberGenome.exe.
/// </summary>
/// <param name="argc">The number of command line arguments passed</param>
/// <param name="argv">The command line arguments passed</param>
/// <returns>0 if successful, else 1.</returns>
int _tmain(int argc, _TCHAR* argv[])
{
bool b = false;
EmberOptions opt;
//Required for large allocs, else GPU memory usage will be severely limited to small sizes.
//This must be done in the application and not in the EmberCL DLL.
#ifdef _WIN32
_putenv_s("GPU_MAX_ALLOC_PERCENT", "100");
#else
putenv(const_cast<char*>("GPU_MAX_ALLOC_PERCENT=100"));
#endif
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
if (!opt.Populate(argc, argv, eOptionUse::OPT_USE_GENOME))
{
auto palf = PaletteList<float>::Instance();
#ifdef DO_DOUBLE
if (!opt.Sp())
b = EmberGenome<double>(argc, argv, opt);
else
#endif
b = EmberGenome<float>(argc, argv, opt);
cout << std::flush;
}
return b ? 0 : 1;
}