#include "EmberPch.h"
#include "EmberToXml.h"
namespace EmberNs
{
///
/// Save the ember to the specified file.
///
/// Full path and filename
/// The ember to save
/// How deep the edit depth goes
/// If true included edit tags, else don't.
/// If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.
/// If true, append to the file if it already exists, else create a new file.
/// Whether a new file is to be started
/// Whether an existing file is to be ended
/// True if successful, else false
template
bool EmberToXml::Save(const string& filename, Ember& ember, size_t printEditDepth, bool doEdits, bool hexPalette, bool append, bool start, bool finish)
{
vector> vec;
vec.push_back(ember);
return Save(filename, vec, printEditDepth, doEdits, hexPalette, append, start, finish);
}
///
/// Save a container of embers to the specified file.
///
/// Full path and filename
/// The container of embers to save
/// How deep the edit depth goes
/// If true included edit tags, else don't.
/// If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.
/// If true, append to the file if it already exists, else create a new file.
/// Whether a new file is to be started
/// Whether an existing file is to be ended
/// True if successful, else false
template
template class C>
bool EmberToXml::Save(const string& filename, C, Alloc>& embers, size_t printEditDepth, bool doEdits, bool hexPalette, bool append, bool start, bool finish)
{
bool b = false;
T t = 0;
string temp;
ofstream f;
try
{
if (append)
f.open(filename, std::ofstream::out | std::ofstream::app);//Appending allows us to write multiple embers to a single file.
else
f.open(filename);
if (f.is_open())
{
//Always ensure times make sense.
for (auto& ember : embers)
ember.m_Time = t++;
if ((append && start) || !append)
{
temp = "\n";
//temp = "\n\n";
f.write(temp.c_str(), temp.size());
}
for (auto& ember : embers)
{
string s = ToString(ember, "", printEditDepth, doEdits, hexPalette);
f.write(s.c_str(), s.size());
}
if ((append && finish) || !append)
{
temp = "\n";
f.write(temp.c_str(), temp.size());
}
f.close();
b = true;
}
else
{
cout << "Error: Writing flame " << filename << " failed.\n";
b = false;
}
}
catch (const std::exception& e)
{
cout << "Error: Writing flame " << filename << " failed: " << e.what() << "\n";
b = false;
}
catch (...)
{
cout << "Error: Writing flame " << filename << " failed.\n";
b = false;
}
return b;
}
///
/// Return the Xml string representation of an ember.
///
/// The ember to create the Xml with
/// If true, add extra attributes, else don't
/// How deep the edit depth goes
/// If true included edit tags, else don't.
/// If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.
/// The Xml string representation of the passed in ember
template
string EmberToXml::ToString(Ember& ember, const string& extraAttributes, size_t printEditDepth, bool doEdits, bool hexPalette)
{
size_t i, j;
string s;
ostringstream os;
vector*> variations;
os << "(1, ember.m_Supersample) << "\"";
os << " filter=\"" << ember.m_SpatialFilterRadius << "\"";
os << " filter_shape=\"" << ToLower(SpatialFilterCreator::ToString(ember.m_SpatialFilterType)) << "\"";
os << " temporal_filter_type=\"" << ToLower(TemporalFilterCreator::ToString(ember.m_TemporalFilterType)) << "\"";
if (ember.m_TemporalFilterType == eTemporalFilterType::EXP_TEMPORAL_FILTER)
os << " temporal_filter_exp=\"" << ember.m_TemporalFilterExp << "\"";
os << " temporal_filter_width=\"" << ember.m_TemporalFilterWidth << "\"";
os << " quality=\"" << ember.m_Quality << "\"";
os << " temporal_samples=\"" << ember.m_TemporalSamples << "\"";
os << " sub_batch_size=\"" << ember.m_SubBatchSize << "\"";
os << " fuse=\"" << ember.m_FuseCount << "\"";
os << " rand_range=\"" << ember.m_RandPointRange << "\"";
os << " background=\"" << ember.m_Background.r << " " << ember.m_Background.g << " " << ember.m_Background.b << "\"";
os << " brightness=\"" << ember.m_Brightness << "\"";
os << " gamma=\"" << ember.m_Gamma << "\"";
os << " highlight_power=\"" << ember.m_HighlightPower << "\"";
os << " logscale_k2=\"" << ember.m_K2 << "\"";
os << " vibrancy=\"" << ember.m_Vibrancy << "\"";
os << " estimator_radius=\"" << ember.m_MaxRadDE << "\"";
os << " estimator_minimum=\"" << ember.m_MinRadDE << "\"";
os << " estimator_curve=\"" << ember.m_CurveDE << "\"";
os << " gamma_threshold=\"" << ember.m_GammaThresh << "\"";
os << " cam_zpos=\"" << ember.m_CamZPos << "\"";
os << " cam_persp=\"" << ember.m_CamPerspective << "\"";
os << " cam_yaw=\"" << ember.m_CamYaw << "\"";
os << " cam_pitch=\"" << ember.m_CamPitch << "\"";
os << " cam_dof=\"" << ember.m_CamDepthBlur << "\"";
os << " blur_curve=\"" << ember.m_BlurCurve << "\"";
os << " stagger=\"" << ember.m_Stagger << "\"";
os << " rotations=\"" << ember.m_Rotations << "\"";
os << " seconds_per_rotation=\"" << ember.m_SecondsPerRotation << "\"";
os << " rotate_xforms_cw=\"" << ember.m_RotateXformsCw << "\"";
os << " blend_seconds=\"" << ember.m_BlendSeconds << "\"";
os << " rotations_per_blend=\"" << ember.m_RotationsPerBlend << "\"";
os << " blend_rotate_xforms_cw=\"" << ember.m_BlendRotateXformsCw << "\"";
os << " linear_blend=\"" << ember.m_Linear << "\"";
if (ember.m_PaletteMode == ePaletteMode::PALETTE_STEP)
os << " palette_mode=\"step\"";
else if (ember.m_PaletteMode == ePaletteMode::PALETTE_LINEAR)
os << " palette_mode=\"linear\"";
if (ember.m_Interp == eInterp::EMBER_INTERP_LINEAR)
os << " interpolation=\"linear\"";
else if (ember.m_Interp == eInterp::EMBER_INTERP_SMOOTH)
os << " interpolation=\"smooth\"";
if (ember.m_AffineInterp == eAffineInterp::AFFINE_INTERP_LINEAR)
os << " interpolation_type=\"linear\"";
else if (ember.m_AffineInterp == eAffineInterp::AFFINE_INTERP_LOG)
os << " interpolation_type=\"log\"";
else if (ember.m_AffineInterp == eAffineInterp::AFFINE_INTERP_COMPAT)
os << " interpolation_type=\"old\"";
else if (ember.m_AffineInterp == eAffineInterp::AFFINE_INTERP_OLDER)
os << " interpolation_type=\"older\"";
if (ember.m_PaletteInterp == ePaletteInterp::INTERP_SWEEP)
os << " palette_interpolation=\"sweep\"";
if (!extraAttributes.empty())
os << " " << extraAttributes;
os << " plugins=\"";
ember.GetPresentVariations(variations, false);
if (!variations.empty())
for (auto var : variations) os << var->Name() << (var != variations.back() ? " " : "\"");
else
os << "\"";
os << " new_linear=\"1\"";
os << " curves=\"";
for (glm::length_t ci = 0; ci < 4; ci++)
{
for (glm::length_t cj = 0; cj < 4; cj++)
{
os << ember.m_Curves.m_Points[ci][cj].x << " ";
os << ember.m_Curves.m_Points[ci][cj].y << " ";
os << "1 ";
}
}
os << "\" overall_curve=\"";
for (glm::length_t cj = 0; cj < ember.m_Curves.m_Points[0].size(); cj++)
{
os << ember.m_Curves.m_Points[0][cj].x << " ";
os << ember.m_Curves.m_Points[0][cj].y << " ";
}
os << "\" red_curve=\"";
for (glm::length_t cj = 0; cj < ember.m_Curves.m_Points[1].size(); cj++)
{
os << ember.m_Curves.m_Points[1][cj].x << " ";
os << ember.m_Curves.m_Points[1][cj].y << " ";
}
os << "\" green_curve=\"";
for (glm::length_t cj = 0; cj < ember.m_Curves.m_Points[2].size(); cj++)
{
os << ember.m_Curves.m_Points[2][cj].x << " ";
os << ember.m_Curves.m_Points[2][cj].y << " ";
}
os << "\" blue_curve=\"";
for (glm::length_t cj = 0; cj < ember.m_Curves.m_Points[3].size(); cj++)
{
os << ember.m_Curves.m_Points[3][cj].x << " ";
os << ember.m_Curves.m_Points[3][cj].y << " ";
}
os << "\">\n";
for (i = 0; i < ember.m_EmberMotionElements.size(); ++i)
os << " " << ToString(ember.m_EmberMotionElements[i]);
//This is a grey area, what to do about symmetry to avoid duplicating the symmetry xforms when reading back?//TODO//BUG.
//if (ember.m_Symmetry)
// os << " \n";
for (i = 0; i < ember.XformCount(); i++)
os << ToString(*ember.GetXform(i), ember.XformCount(), false, false);//Not final, don't do motion.
if (ember.UseFinalXform())
os << ToString(*ember.NonConstFinalXform(), ember.XformCount(), true, false);//Final, don't do motion.
//Note that only embedded palettes are saved. The old style of specifying a palette index to look up in a default palette file
//is no longer supported, as it makes no sense when using multiple palette files. The only way it could work is if the index was
//always meant to refer to the default file, or if the filename was embedded as well. It's easier, more straightforward and
//less error prone to just embed the palette.
if (hexPalette)
{
os << " \n";
os << std::uppercase;
const auto rows = ember.m_Palette.Size() / 8;
for (i = 0; i < rows; i++)
{
os << " ";
for (j = 0; j < 8; j++)
{
size_t idx = 8 * i + j;
os << hex << setw(2) << setfill('0') << static_cast(std::rint(ember.m_Palette[idx][0] * 255));
os << hex << setw(2) << setfill('0') << static_cast(std::rint(ember.m_Palette[idx][1] * 255));
os << hex << setw(2) << setfill('0') << static_cast(std::rint(ember.m_Palette[idx][2] * 255));
}
os << "\n";
}
os << std::nouppercase;
os << " \n";
}
else
{
for (i = 0; i < ember.m_Palette.Size(); i++)
{
const double r = ember.m_Palette[i][0] * 255;
const double g = ember.m_Palette[i][1] * 255;
const double b = ember.m_Palette[i][2] * 255;
const double a = ember.m_Palette[i][3] * 255;
os << " ";
//The original used a precision of 6 which is totally unnecessary, use 2.
if (IsClose(a, 255.0))
os << "";
else
os << " ";
os << "\n";
}
}
if (doEdits && ember.m_Edits)
os << ToString(xmlDocGetRootElement(ember.m_Edits), 1, true, printEditDepth);
os << "\n";
return os.str();
}
///
/// Create a new editdoc optionally based on parents passed in.
/// This is used when an ember is made out of some mutation or edit from one or two existing embers and
/// the user wants to capture the genetic lineage history information in the edit doc of the new ember.
///
/// The first parent, optionally nullptr.
/// The second parent, optionally nullptr.
/// The action that was taken to create the new ember
/// The nickname of the author
/// The Url of the author
/// The id of the author
/// The comment to include
/// The sheep generation used if > 0. Default: 0.
/// The sheep id used if > 0. Default: 0.
///
template
xmlDocPtr EmberToXml::CreateNewEditdoc(Ember* parent0, Ember* parent1, const string& action, const string& nick, const string& url, const string& id, const string& comment, intmax_t sheepGen, intmax_t sheepId)
{
char timeString[128];
time_t myTime;
string s;
xmlDocPtr commentDoc = nullptr;
xmlDocPtr doc = xmlNewDoc(XC("1.0"));
xmlNodePtr rootNode = nullptr, node = nullptr, nodeCopy = nullptr;
xmlNodePtr rootComment = nullptr;
ostringstream os;
//Create the root node, called "edit".
rootNode = xmlNewNode(nullptr, XC("edit"));
xmlDocSetRootElement(doc, rootNode);
//Add the edit attributes.
//Date.
myTime = time(nullptr);
#ifdef _WIN32
tm localt;
localtime_s(&localt, &myTime);
strftime(timeString, 128, "%a %b %d %H:%M:%S %z %Y", &localt);//XXX use standard time format including timezone.
#else
tm* localt;
localt = localtime(&myTime);
strftime(timeString, 128, "%a %b %d %H:%M:%S %z %Y", localt);//XXX use standard time format including timezone.
#endif
xmlNewProp(rootNode, XC("date"), XC(timeString));
//Nick.
if (nick != "")
xmlNewProp(rootNode, XC("nick"), XC(nick.c_str()));
//Url.
if (url != "")
xmlNewProp(rootNode, XC("url"), XC(url.c_str()));
if (id != "")
xmlNewProp(rootNode, XC("id"), XC(id.c_str()));
//Action.
xmlNewProp(rootNode, XC("action"), XC(action.c_str()));
//Sheep info.
if (sheepGen > 0 && sheepId > 0)
{
//Create a child node of the root node called sheep.
node = xmlNewChild(rootNode, nullptr, XC("sheep"), nullptr);
//Create the sheep attributes.
os << sheepGen;
s = os.str();
xmlNewProp(node, XC("generation"), XC(s.c_str()));
os.str("");
os << sheepId;
s = os.str();
xmlNewProp(node, XC("id"), XC(s.c_str()));
os.str("");
}
//Check for the parents.
//If parent 0 not specified, this is a randomly generated genome.
if (parent0)
{
os << parent0->m_Index;
s = os.str();
if (parent0->m_Edits)
{
//Copy the node from the parent.
node = xmlDocGetRootElement(parent0->m_Edits);
nodeCopy = xmlCopyNode(node, 1);
AddFilenameWithoutAmpersand(nodeCopy, parent0->m_ParentFilename);
xmlNewProp(nodeCopy, XC("index"), XC(s.c_str()));
xmlAddChild(rootNode, nodeCopy);
}
else
{
//Insert a (parent has no edit) message.
nodeCopy = xmlNewChild(rootNode, nullptr, XC("edit"), nullptr);
AddFilenameWithoutAmpersand(nodeCopy, parent0->m_ParentFilename);
xmlNewProp(nodeCopy, XC("index"), XC(s.c_str()));
}
os.str("");
}
if (parent1)
{
os << parent1->m_Index;
s = os.str();
if (parent1->m_Edits)
{
//Copy the node from the parent.
node = xmlDocGetRootElement(parent1->m_Edits);
nodeCopy = xmlCopyNode(node, 1);
AddFilenameWithoutAmpersand(nodeCopy, parent1->m_ParentFilename);
xmlNewProp(nodeCopy, XC("index"), XC(s.c_str()));
xmlAddChild(rootNode, nodeCopy);
}
else
{
//Insert a (parent has no edit) message.
nodeCopy = xmlNewChild(rootNode, nullptr, XC("edit"), nullptr);
AddFilenameWithoutAmpersand(nodeCopy, parent1->m_ParentFilename);
xmlNewProp(nodeCopy, XC("index"), XC(s.c_str()));
}
os.str("");
}
//Comment string:
//This one's hard, since the comment string must be treated as
//a valid XML document. Create a new document using the comment
//string as the in-memory document, and then copy all children of
//the root node into the edit structure
//Parsing the comment string should be done once and then copied
//for each new edit doc, but that's for later.
if (comment != "")
{
os << "" << comment << "";
s = os.str();
commentDoc = xmlReadMemory(s.c_str(), static_cast(s.length()), "comment.env", nullptr, XML_PARSE_NONET);
os.str("");
//Check for errors.
if (commentDoc)
{
//Loop through the children of the new document and copy them into the rootNode.
rootComment = xmlDocGetRootElement(commentDoc);
for (node = rootComment->children; node; node = node->next)
{
nodeCopy = xmlCopyNode(node, 1);
xmlAddChild(rootNode, nodeCopy);
}
//Free the created document.
xmlFreeDoc(commentDoc);
}
else
{
cout << "Failed to parse comment into Xml.\n";
}
}
//Return the Xml doc.
return doc;
}
///
/// Return the Xml string representation of an xform.
///
/// The xform to create the Xml with
/// The number of non-final xforms in the ember to which this xform belongs. Used for xaos.
/// True if the xform is the final xform in the ember, else false.
/// If true, include motion elements in the Xml string, else omit.
/// The Xml string representation of the passed in xform
template
string EmberToXml::ToString(Xform& xform, size_t xformCount, bool isFinal, bool doMotion)
{
size_t i, j;
ostringstream os;
os << std::fixed << std::setprecision(8);
if (doMotion)
{
os << " * var = xform.GetVariation(i);
ParametricVariation* parVar = dynamic_cast*>(var);
if (var->m_Weight != 0)
{
os << var->Name() << "=\"" << var->m_Weight << "\" ";
if (parVar)
{
auto params = parVar->Params();
for (j = 0; j < parVar->ParamCount(); j++)
{
if ((!doMotion || (doMotion && (params[j].ParamVal() != 0))) && !params[j].IsPrecalc())
os << params[j].Name() << "=\"" << params[j].ParamVal() << "\" ";
}
}
}
}
if (!doMotion || (doMotion && !xform.m_Affine.IsZero() && !xform.m_Affine.IsEmpty()))
{
os << "coefs=\"" << xform.m_Affine.A() << " " << xform.m_Affine.D() << " " << xform.m_Affine.B() << " "
<< xform.m_Affine.E() << " " << xform.m_Affine.C() << " " << xform.m_Affine.F() << "\"";
}
if ((!doMotion && !xform.m_Post.IsID()) || (doMotion && !xform.m_Post.IsZero() && !xform.m_Post.IsEmpty()))
{
os << " post=\"" << xform.m_Post.A() << " " << xform.m_Post.D() << " " << xform.m_Post.B() << " "
<< xform.m_Post.E() << " " << xform.m_Post.C() << " " << xform.m_Post.F() << "\"";
}
//Original only printed xaos values that were not 1. Here, print them all out if any are present.
if (!isFinal && !doMotion && xform.XaosPresent())//Applying motion to xaos not supported.
{
os << " chaos=\"";
for (i = 0; i < xformCount; i++)
os << xform.Xaos(i) << " ";
os << "\"";
}
if (!doMotion || xform.m_Opacity != EMPTYFIELD) os << " opacity=\"" << xform.m_Opacity << "\"";
if (!doMotion && !xform.m_Motion.empty())
{
os << ">\n";
for (i = 0; i < xform.m_Motion.size(); i++)
os << ToString(xform.m_Motion[i], 0, false, true);
if (isFinal)//Fixed to properly close final.//SMOULDER
os << " \n";
else
os << " \n";
}
else
os << "/>\n";
return os.str();
}
///
/// Return an edit node Xml string.
///
/// The edit node to get the string for
/// How many tabs to use
/// If true, include newlines and tabs, else don't.
/// How deep the edit depth goes
/// The edit node Xml string
template
string EmberToXml::ToString(xmlNodePtr editNode, size_t tabs, bool formatting, size_t printEditDepth)
{
bool indentPrinted = false;
const char* tabString = " ", *attStr;
const char* editString = "edit";
const char* sheepString = "sheep";
size_t ti;//, editOrSheep = 0;
xmlAttrPtr attPtr = nullptr, curAtt = nullptr;
xmlNodePtr childPtr = nullptr, curChild = nullptr;
ostringstream os;
if (printEditDepth > 0 && tabs > printEditDepth)
return "";
//If this node is an XML_ELEMENT_NODE, print it and its attributes.
if (editNode->type == XML_ELEMENT_NODE)
{
//Print the node at the tab specified.
if (formatting)
for (ti = 0; ti < tabs; ti++)
os << tabString;
os << "<" << editNode->name;
//This can either be an edit node or a sheep node.
//If it's an edit node, add one to the tab.
if (!Compare(editNode->name, editString))
{
//editOrSheep = 1;
tabs++;
}
else if (!Compare(editNode->name, sheepString)) {}
//editOrSheep = 2;
else {}
//editOrSheep = 0;
//Print the attributes.
attPtr = editNode->properties;
for (curAtt = attPtr; curAtt; curAtt = curAtt->next)
{
attStr = CX(xmlGetProp(editNode, curAtt->name));
os << " " << curAtt->name << "=\"" << attStr << "\"";
xmlFree(reinterpret_cast(const_cast(attStr)));
}
//Does this node have children?
if (!editNode->children || (printEditDepth > 0 && tabs > printEditDepth))
{
//Close the tag and subtract the tab.
os << "/>";
if (formatting)
os << "\n";
tabs--;
}
else
{
//Close the tag.
os << ">";
if (formatting)
os << "\n";
//Loop through the children and print them.
childPtr = editNode->children;
indentPrinted = false;
for (curChild = childPtr; curChild; curChild = curChild->next)
{
//If child is an element, indent first and then print it.
if (curChild->type == XML_ELEMENT_NODE &&
(!Compare(curChild->name, editString) || !Compare(curChild->name, sheepString)))
{
if (indentPrinted)
{
indentPrinted = false;
os << "\n";
}
os << ToString(curChild, tabs, true, printEditDepth);
}
else
{
//Child is a text node, don't want to indent more than once.
if (xmlIsBlankNode(curChild))
continue;
if (!indentPrinted && formatting)
{
for (ti = 0; ti < tabs; ti++)
os << tabString;
indentPrinted = true;
}
//Print nodes without formatting.
os << ToString(curChild, tabs, false, printEditDepth);
}
}
if (indentPrinted && formatting)
os << "\n";
tabs--;//Tab out.
if (formatting)
for (ti = 0; ti < tabs; ti++)
os << tabString;
os << "" << editNode->name << ">";//Close the tag.
if (formatting)
os << "\n";
}
}
else if (editNode->type == XML_TEXT_NODE)
{
string s(reinterpret_cast(xmlNodeGetContent(editNode)));
os << Trim(s);
}
return os.str();
}
///
/// Convert a EmberMotion element to an xml string
///
/// The EmberMotion object to convert to XML
/// The string representation of the passed in EmberMotion object
template
string EmberToXml::ToString(const EmberMotion& motion)
{
ostringstream os;
os << " 0)
os << "motion_offset=\"" << motion.m_MotionOffset << "\" ";
os << "motion_func=";
switch (motion.m_MotionFunc)
{
case eMotion::MOTION_SIN:
os << "\"sin\"";
break;
case eMotion::MOTION_HILL:
os << "\"hill\"";
break;
case eMotion::MOTION_TRIANGLE:
os << "\"triangle\"";
break;
case eMotion::MOTION_SAW:
default:
os << "\"saw\"";
break;
}
T r = 0.0;
T g = 0.0;
T b = 0.0;
T cx = 0.0;
T cy = 0.0;
for (size_t i = 0; i < motion.m_MotionParams.size(); ++i)
{
switch (motion.m_MotionParams[i].first)
{
case eEmberMotionParam::FLAME_MOTION_ZOOM:
os << " zoom=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_ZPOS:
os << " cam_zpos=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_PERSPECTIVE:
os << " cam_persp=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_YAW:
os << " cam_yaw=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_PITCH:
os << " cam_pitch=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_DEPTH_BLUR:
os << " cam_dof=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_CENTER_X:
cx = motion.m_MotionParams[i].second;
break;
case eEmberMotionParam::FLAME_MOTION_CENTER_Y:
cy = motion.m_MotionParams[i].second;
break;
case eEmberMotionParam::FLAME_MOTION_ROTATE:
os << " rotate=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_BRIGHTNESS:
os << " brightness=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_GAMMA:
os << " gamma=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_GAMMA_THRESH:
os << " gamma_threshold=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_HIGHLIGHT_POWER:
os << " highlight_power=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_K2:
os << " logscale_k2=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_RAND_RANGE:
os << " rand_range=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_BACKGROUND_R:
r = motion.m_MotionParams[i].second;
break;
case eEmberMotionParam::FLAME_MOTION_BACKGROUND_G:
g = motion.m_MotionParams[i].second;
break;
case eEmberMotionParam::FLAME_MOTION_BACKGROUND_B:
b = motion.m_MotionParams[i].second;
break;
case eEmberMotionParam::FLAME_MOTION_VIBRANCY:
os << " vibrancy=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_SCALE:
os << " scale=\"" << motion.m_MotionParams[i].second << "\"";
break;
case eEmberMotionParam::FLAME_MOTION_NONE:
default:
break;
}
}
if (r != 0.0 || g != 0.0 || b != 0.0)
os << " background=\"" << r << " " << g << " " << b << "\"";
if (cx != 0.0 || cy != 0.0)
os << " center=\"" << cx << " " << cy << "\"";
os << "/>\n";
return os.str();
}
///
/// Replace the character '&' in a filename with "&" and set the value in
/// the passed in node to this new string.
/// If no '&' is found, the passed in string is used as-is when populating the value of the node.
/// This is done because Xml parsing can't handle ampersands.
///
/// The XML node to write the new value to
/// The filename string to examine for ampersands.
template
void EmberToXml::AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename)
{
if (filename.find_first_of('&') != std::string::npos)
{
string filenameWithoutAmpersands = filename;
FindAndReplace(filenameWithoutAmpersands, "&", "&");
xmlNewProp(node, XC("filename"), XC(filenameWithoutAmpersands.c_str()));
}
else
{
xmlNewProp(node, XC("filename"), XC(filename.c_str()));
}
}
#define EXPORT_EMBER_TO_XML(T) \
template EMBER_API class EmberToXml; \
template EMBER_API bool EmberToXml::Save(const string&, vector>& embers, size_t, bool, bool, bool, bool, bool); \
template EMBER_API bool EmberToXml::Save(const string&, list>& embers, size_t, bool, bool, bool, bool, bool);
EXPORT_EMBER_TO_XML(float)
#ifdef DO_DOUBLE
EXPORT_EMBER_TO_XML(double)
#endif
}