mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-10-07 21:50:51 -04:00
--User changes
-Add a palette editor. -Add support for reading .ugr/.gradient/.gradients palette files. -Allow toggling on spinners whose minimum value is not zero. -Allow toggling display of image, affines and grid. -Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial. --Bug fixes -cpow2 was wrong. -Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3. -Use exec() on Apple and show() on all other OSes for dialog boxes. -Trying to render a sequence with no frames would crash. -Selecting multiple xforms and rotating them would produce the wrong rotation. -Better handling when parsing flames using different encoding, such as unicode and UTF-8. -Switching between SP/DP didn't reselect the selected flame in the Library tab. --Code changes -Make all types concerning palettes be floats, including PaletteTableWidgetItem. -PaletteTableWidgetItem is no longer templated because all palettes are float. -Include the source colors for user created gradients. -Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems. -Split conditional out of accumulation loop on the CPU for better performance. -Vectorize summing when doing density filter for better performance. -Make all usage of palettes be of type float, double is pointless. -Allow palettes to reside in multiple folders, while ensuring only one of each name is added. -Refactor some palette path searching code. -Make ReadFile() throw and catch an exception if the file operation fails. -A little extra safety in foci and foci3D with a call to Zeps(). -Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac. -Fixing missing comma between paths in InitPaletteList(). -Move Xml and PaletteList classes into cpp to shorten build times when working on them. -Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file. -Change more NULL to nullptr.
This commit is contained in:
@ -203,6 +203,19 @@ void Affine2D<T>::Scale(T amount)
|
||||
F(F() * amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales all values A,B,D,E by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount to scale by</param>
|
||||
template <typename T>
|
||||
void Affine2D<T>::ScaleXY(T amount)
|
||||
{
|
||||
A(A() * amount);
|
||||
B(B() * amount);
|
||||
D(D() * amount);
|
||||
E(E() * amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotate this affine transform around its origin by the specified angle in degrees.
|
||||
/// </summary>
|
||||
|
@ -76,6 +76,7 @@ public:
|
||||
bool IsZero() const;
|
||||
bool IsEmpty() const;
|
||||
void Scale(T amount);
|
||||
void ScaleXY(T amount);
|
||||
Affine2D<T> ScaleCopy(T amount);
|
||||
void Rotate(T rad);
|
||||
void RotateTrans(T rad);
|
||||
|
@ -396,6 +396,11 @@ uint Timing::m_ProcessorCount;
|
||||
EXPORTPREPOSTREGVAR(Gamma, T) \
|
||||
EXPORTPREPOSTREGVAR(PRose3D, T) \
|
||||
EXPORTPREPOSTREGVAR(LogDB, T) \
|
||||
EXPORTPREPOSTREGVAR(CircleSplit, T) \
|
||||
EXPORTPREPOSTREGVAR(Cylinder2, T) \
|
||||
EXPORTPREPOSTREGVAR(TileLog, T) \
|
||||
EXPORTPREPOSTREGVAR(TruchetFill, T) \
|
||||
EXPORTPREPOSTREGVAR(Waves2Radial, T) \
|
||||
template EMBER_API class PostSmartcropVariation<T>; /*Only implemented as post.*/ \
|
||||
EXPORTPREPOSTREGVAR(DCBubble, T) \
|
||||
EXPORTPREPOSTREGVAR(DCCarpet, T) \
|
||||
@ -436,11 +441,10 @@ uint Timing::m_ProcessorCount;
|
||||
template EMBER_API class XmlToEmber<T>; \
|
||||
template EMBER_API class EmberToXml<T>;
|
||||
|
||||
EXPORT_SINGLE_TYPE_EMBER(float)
|
||||
|
||||
#define EXPORT_TWO_TYPE_EMBER(T, bucketT) \
|
||||
template EMBER_API class SheepTools<T, bucketT>;
|
||||
|
||||
EXPORT_SINGLE_TYPE_EMBER(float)
|
||||
EXPORT_TWO_TYPE_EMBER(float, float)
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
|
@ -659,23 +659,23 @@ public:
|
||||
{
|
||||
for (glm::length_t i = 0; i < 256; i++)
|
||||
{
|
||||
T t[3], s[4] = { 0, 0, 0, 0 };
|
||||
float t[3], s[4] = { 0, 0, 0, 0 };
|
||||
|
||||
for (glm::length_t k = 0; k < size; k++)
|
||||
{
|
||||
Palette<T>::RgbToHsv(glm::value_ptr(embers[k].m_Palette[i]), t);
|
||||
Palette<float>::RgbToHsv(glm::value_ptr(embers[k].m_Palette[i]), t);
|
||||
|
||||
for (size_t j = 0; j < 3; j++)
|
||||
s[j] += coefs[k] * t[j];
|
||||
s[j] += float(coefs[k]) * t[j];
|
||||
|
||||
s[3] += coefs[k] * embers[k].m_Palette[i][3];
|
||||
s[3] += float(coefs[k]) * embers[k].m_Palette[i][3];
|
||||
}
|
||||
|
||||
Palette<T>::HsvToRgb(s, glm::value_ptr(m_Palette[i]));
|
||||
Palette<float>::HsvToRgb(s, glm::value_ptr(m_Palette[i]));
|
||||
m_Palette[i][3] = s[3];
|
||||
|
||||
for (glm::length_t j = 0; j < 4; j++)
|
||||
Clamp<T>(m_Palette[i][j], 0, 1);
|
||||
Clamp<float>(m_Palette[i][j], 0, 1);
|
||||
}
|
||||
}
|
||||
else if (embers[0].m_PaletteInterp == ePaletteInterp::INTERP_SWEEP)
|
||||
@ -1100,8 +1100,8 @@ public:
|
||||
/// <summary>
|
||||
/// Placeholder to do nothing.
|
||||
/// </summary>
|
||||
/// <param name="point">Unused</param>
|
||||
/// <param name="rand">Unused</param>
|
||||
/// <param name="point">Ignored</param>
|
||||
/// <param name="rand">Ignored</param>
|
||||
void ProjectNone(Point<T>& point, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
}
|
||||
@ -1110,7 +1110,7 @@ public:
|
||||
/// Project when only z is set, and not pitch, yaw, projection or depth blur.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to project</param>
|
||||
/// <param name="rand">Unused</param>
|
||||
/// <param name="rand">Ignored</param>
|
||||
void ProjectZPerspective(Point<T>& point, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
T zr = Zeps(1 - m_CamPerspective * (point.m_Z - m_CamZPos));
|
||||
@ -1123,7 +1123,7 @@ public:
|
||||
/// Project when pitch, and optionally z and perspective are set, but not depth blur or yaw.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to project</param>
|
||||
/// <param name="rand">Unused</param>
|
||||
/// <param name="rand">Ignored</param>
|
||||
void ProjectPitch(Point<T>& point, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
T z = point.m_Z - m_CamZPos;
|
||||
@ -1180,7 +1180,7 @@ public:
|
||||
/// Project when yaw and optionally pitch, z, and perspective are set, but not depth blur.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to project</param>
|
||||
/// <param name="rand">Unused</param>
|
||||
/// <param name="rand">Ignored</param>
|
||||
void ProjectPitchYaw(Point<T>& point, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
T z = point.m_Z - m_CamZPos;
|
||||
@ -1653,7 +1653,7 @@ public:
|
||||
//The color palette to use. Can be specified inline as Xml color fields, or as a hex buffer. Can also be specified
|
||||
//as an index into the palette file with an optional hue rotation applied. Inserting as a hex buffer is the preferred method.
|
||||
//Xml field: "color" or "colors" or "palette" .
|
||||
Palette<T> m_Palette;//Final palette that is actually used is a copy of this inside of render, which will be of type bucketT (float).
|
||||
Palette<float> m_Palette;//Final palette that is actually used is a copy of this inside of render, which will be of type bucketT (float).
|
||||
|
||||
//Curves used to adjust the color during final accumulation.
|
||||
Curves<T> m_Curves;
|
||||
|
@ -103,6 +103,8 @@ static inline size_t NowMs()
|
||||
#define v2T glm::tvec2<T, glm::defaultp>
|
||||
#define v3T glm::tvec3<T, glm::defaultp>
|
||||
#define v4T glm::tvec4<T, glm::defaultp>
|
||||
#define v4F glm::tvec4<float, glm::defaultp>
|
||||
#define v4D glm::tvec4<double, glm::defaultp>
|
||||
#define v4bT glm::tvec4<bucketT, glm::defaultp>
|
||||
#define m2T glm::tmat2x2<T, glm::defaultp>
|
||||
#define m3T glm::tmat3x3<T, glm::defaultp>
|
||||
@ -112,6 +114,8 @@ static inline size_t NowMs()
|
||||
#define v2T glm::detail::tvec2<T, glm::defaultp>
|
||||
#define v3T glm::detail::tvec3<T, glm::defaultp>
|
||||
#define v4T glm::detail::tvec4<T, glm::defaultp>
|
||||
#define v4F glm::detail::tvec4<float, glm::defaultp>
|
||||
#define v4D glm::detail::tvec4<double, glm::defaultp>
|
||||
#define v4bT glm::detail::tvec4<bucketT, glm::defaultp>
|
||||
#define m2T glm::detail::tmat2x2<T, glm::defaultp>
|
||||
#define m3T glm::detail::tmat3x3<T, glm::defaultp>
|
||||
|
880
Source/Ember/EmberToXml.cpp
Normal file
880
Source/Ember/EmberToXml.cpp
Normal file
@ -0,0 +1,880 @@
|
||||
#include "EmberPch.h"
|
||||
#include "EmberToXml.h"
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Save the ember to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Full path and filename</param>
|
||||
/// <param name="ember">The ember to save</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <param name="append">If true, append to the file if it already exists, else create a new file.</param>
|
||||
/// <param name="start">Whether a new file is to be started</param>
|
||||
/// <param name="finish">Whether an existing file is to be ended</param>
|
||||
/// <returns>True if successful, else false</returns>
|
||||
template <typename T>
|
||||
bool EmberToXml<T>::Save(const string& filename, Ember<T>& ember, size_t printEditDepth, bool doEdits, bool hexPalette, bool append, bool start, bool finish)
|
||||
{
|
||||
vector<Ember<T>> vec;
|
||||
vec.push_back(ember);
|
||||
return Save(filename, vec, printEditDepth, doEdits, hexPalette, append, start, finish);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save a container of embers to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Full path and filename</param>
|
||||
/// <param name="embers">The container of embers to save</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <param name="append">If true, append to the file if it already exists, else create a new file.</param>
|
||||
/// <param name="start">Whether a new file is to be started</param>
|
||||
/// <param name="finish">Whether an existing file is to be ended</param>
|
||||
/// <returns>True if successful, else false</returns>
|
||||
template <typename T>
|
||||
template <typename Alloc, template <typename, typename> class C>
|
||||
bool EmberToXml<T>::Save(const string& filename, C<Ember<T>, 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())
|
||||
{
|
||||
auto prev = embers.begin();
|
||||
|
||||
//Always ensure times make sense.
|
||||
for (auto& ember : embers)
|
||||
ember.m_Time = t++;
|
||||
|
||||
if ((append && start) || !append)
|
||||
{
|
||||
temp = "<flames>\n";
|
||||
//temp = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<flames>\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 = "</flames>\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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the Xml string representation of an ember.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to create the Xml with</param>
|
||||
/// <param name="extraAttributes">If true, add extra attributes, else don't</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <returns>The Xml string representation of the passed in ember</returns>
|
||||
template <typename T>
|
||||
string EmberToXml<T>::ToString(Ember<T>& ember, const string& extraAttributes, size_t printEditDepth, bool doEdits, bool hexPalette)
|
||||
{
|
||||
size_t i, j;
|
||||
string s;
|
||||
ostringstream os;
|
||||
vector<Variation<T>*> variations;
|
||||
os << "<flame version=\"EMBER-" << EmberVersion() << "\" time=\"" << ember.m_Time << "\"";
|
||||
|
||||
if (!ember.m_Name.empty())
|
||||
os << " name=\"" << ember.m_Name << "\"";
|
||||
|
||||
os << " size=\"" << ember.m_FinalRasW << " " << ember.m_FinalRasH << "\"";
|
||||
os << " center=\"" << ember.m_CenterX << " " << ember.m_CenterY << "\"";
|
||||
os << " scale=\"" << ember.m_PixelsPerUnit << "\"";
|
||||
|
||||
if (ember.m_Zoom != 0)
|
||||
os << " zoom=\"" << ember.m_Zoom << "\"";
|
||||
|
||||
os << " rotate=\"" << ember.m_Rotate << "\"";
|
||||
os << " supersample=\"" << std::max<size_t>(1, ember.m_Supersample) << "\"";
|
||||
os << " filter=\"" << ember.m_SpatialFilterRadius << "\"";
|
||||
os << " filter_shape=\"" << ToLower(SpatialFilterCreator<T>::ToString(ember.m_SpatialFilterType)) << "\"";
|
||||
os << " temporal_filter_type=\"" << ToLower(TemporalFilterCreator<T>::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 << " 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 << " 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 << "\"";
|
||||
|
||||
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 << ember.m_Curves.m_Weights[ci][cj] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
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 << " <symmetry kind=\"" << ember.m_Symmetry << "\"/>\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 << " <palette count=\"256\" format=\"RGB\"";
|
||||
|
||||
if (!ember.m_Palette.m_SourceColors.empty())
|
||||
{
|
||||
os << " source_colors=\"";
|
||||
|
||||
for (auto& sc : ember.m_Palette.m_SourceColors)
|
||||
{
|
||||
os <<
|
||||
Clamp(sc.first, 0.0f, 1.0f) << "," <<
|
||||
Clamp(sc.second.r, 0.0f, 1.0f) << "," <<
|
||||
Clamp(sc.second.g, 0.0f, 1.0f) << "," <<
|
||||
Clamp(sc.second.b, 0.0f, 1.0f) << " ";
|
||||
}
|
||||
|
||||
os << "\"";
|
||||
}
|
||||
|
||||
os << ">\n";
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
os << " ";
|
||||
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
size_t idx = 8 * i + j;
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(ember.m_Palette[idx][0] * 255));
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(ember.m_Palette[idx][1] * 255));
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(ember.m_Palette[idx][2] * 255));
|
||||
}
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
os << " </palette>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
double r = ember.m_Palette[i][0] * 255;
|
||||
double g = ember.m_Palette[i][1] * 255;
|
||||
double b = ember.m_Palette[i][2] * 255;
|
||||
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 << "<color index=\"" << i << "\" rgb=\"" << std::fixed << std::setprecision(2) << r << " " << g << " " << b << "\"/>";
|
||||
else
|
||||
os << " <color index=\"" << i << "\" rgba=\"" << std::fixed << std::setprecision(2) << r << " " << g << " " << b << " " << a << "\"/>";
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (doEdits && ember.m_Edits)
|
||||
os << ToString(xmlDocGetRootElement(ember.m_Edits), 1, true, printEditDepth);
|
||||
|
||||
os << "</flame>\n";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="parent0">The first parent, optionally nullptr.</param>
|
||||
/// <param name="parent1">The second parent, optionally nullptr.</param>
|
||||
/// <param name="action">The action that was taken to create the new ember</param>
|
||||
/// <param name="nick">The nickname of the author</param>
|
||||
/// <param name="url">The Url of the author</param>
|
||||
/// <param name="id">The id of the author</param>
|
||||
/// <param name="comment">The comment to include</param>
|
||||
/// <param name="sheepGen">The sheep generation used if > 0. Default: 0.</param>
|
||||
/// <param name="sheepId">The sheep id used if > 0. Default: 0.</param>
|
||||
/// <returns></returns>
|
||||
template <typename T>
|
||||
xmlDocPtr EmberToXml<T>::CreateNewEditdoc(Ember<T>* parent0, Ember<T>* 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 << "<comm>" << comment << "</comm>";
|
||||
s = os.str();
|
||||
commentDoc = xmlReadMemory(s.c_str(), int(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the Xml string representation of an xform.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform to create the Xml with</param>
|
||||
/// <param name="xformCount">The number of non-final xforms in the ember to which this xform belongs. Used for xaos.</param>
|
||||
/// <param name="isFinal">True if the xform is the final xform in the ember, else false.</param>
|
||||
/// <param name="doMotion">If true, include motion elements in the Xml string, else omit.</param>
|
||||
/// <returns>The Xml string representation of the passed in xform</returns>
|
||||
template <typename T>
|
||||
string EmberToXml<T>::ToString(Xform<T>& xform, size_t xformCount, bool isFinal, bool doMotion)
|
||||
{
|
||||
size_t i, j;
|
||||
ostringstream os;
|
||||
|
||||
if (doMotion)
|
||||
{
|
||||
os << " <motion motion_frequency=\"" << xform.m_MotionFreq << "\" ";
|
||||
|
||||
if (xform.m_MotionFunc == eMotion::MOTION_SIN)
|
||||
os << "motion_function=\"sin\" ";
|
||||
else if (xform.m_MotionFunc == eMotion::MOTION_TRIANGLE)
|
||||
os << "motion_function=\"triangle\" ";
|
||||
else if (xform.m_MotionFunc == eMotion::MOTION_HILL)
|
||||
os << "motion_function=\"hill\" ";
|
||||
else if (xform.m_MotionFunc == eMotion::MOTION_SAW)
|
||||
os << "motion_function=\"saw\" ";
|
||||
|
||||
if (xform.m_MotionOffset != 0)
|
||||
os << "motion_offset=\"" << xform.m_MotionOffset << "\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isFinal)
|
||||
os << " <finalxform ";
|
||||
else
|
||||
os << " <xform weight=\"" << xform.m_Weight << "\" ";
|
||||
}
|
||||
|
||||
if (!doMotion || xform.m_ColorX != EMPTYFIELD) os << "color=\"" << xform.m_ColorX << "\" ";
|
||||
|
||||
//if (!doMotion || xform.m_ColorY != EMPTYFIELD) os << "color=\"" << xform.m_ColorX << " " << xform.m_ColorY << "\" ";
|
||||
if (!doMotion || xform.m_DirectColor != EMPTYFIELD) os << "var_color=\"" << xform.m_DirectColor << "\" ";
|
||||
|
||||
if (!doMotion || xform.m_ColorSpeed != EMPTYFIELD) os << "color_speed=\"" << xform.m_ColorSpeed << "\" ";
|
||||
|
||||
//os << "symmetry=\"" << fabs(xform.m_ColorSpeed - 1) * 2 << "\" ";//Legacy support.
|
||||
|
||||
if (!doMotion)
|
||||
{
|
||||
string s = xform.m_Name;
|
||||
std::replace(s.begin(), s.end(), ' ', '_');
|
||||
os << "name=\"" << s << "\" ";//Flam3 didn't do this, but Apo does.
|
||||
|
||||
if (!isFinal)
|
||||
os << "animate=\"" << xform.m_Animate << "\" ";
|
||||
}
|
||||
|
||||
//Variation writing order differs slightly from the original to make it a bit more readable.
|
||||
//The original wrote out all of the variation names and weights. Then wrote out the parameters for
|
||||
//the parametric variations. Here, write out the params immediately after each parametric variation
|
||||
//so they are more closely grouped with the variation they apply to, rather than being all grouped at the end.
|
||||
for (i = 0; i < xform.TotalVariationCount(); i++)
|
||||
{
|
||||
Variation<T>* var = xform.GetVariation(i);
|
||||
ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(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 << " </finalxform>\n";
|
||||
else
|
||||
os << " </xform>\n";
|
||||
}
|
||||
else
|
||||
os << "/>\n";
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an edit node Xml string.
|
||||
/// </summary>
|
||||
/// <param name="editNode">The edit node to get the string for</param>
|
||||
/// <param name="tabs">How many tabs to use</param>
|
||||
/// <param name="formatting">If true, include newlines and tabs, else don't.</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <returns>The edit node Xml string</returns>
|
||||
template <typename T>
|
||||
string EmberToXml<T>::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<void*>(const_cast<char*>(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<char*>(xmlNodeGetContent(editNode)));
|
||||
os << Trim(s);
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a EmberMotion element to an xml string
|
||||
/// </summary>
|
||||
/// <param name="motion">The EmberMotion object to convert to XML</param>
|
||||
/// <returns>The string representation of the passed in EmberMotion object</returns>
|
||||
template <typename T>
|
||||
string EmberToXml<T>::ToString(const EmberMotion<T>& motion)
|
||||
{
|
||||
ostringstream os;
|
||||
os << "<flame_motion motion_frequency=\"" << motion.m_MotionFreq << "\" ";
|
||||
|
||||
if (motion.m_MotionOffset > 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_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_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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="node">The XML node to write the new value to</param>
|
||||
/// <param name="filename">The filename string to examine for ampersands.</param>
|
||||
template <typename T>
|
||||
void EmberToXml<T>::AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename)
|
||||
{
|
||||
if (filename.find_first_of('&') != std::string::npos)
|
||||
{
|
||||
string filenameWithoutAmpersands = filename;
|
||||
FindAndReplace<string>(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<T>; \
|
||||
template EMBER_API bool EmberToXml<T>::Save(const string&, vector<Ember<T>>& embers, size_t, bool, bool, bool, bool, bool); \
|
||||
template EMBER_API bool EmberToXml<T>::Save(const string&, list<Ember<T>>& embers, size_t, bool, bool, bool, bool, bool);
|
||||
|
||||
EXPORT_EMBER_TO_XML(float)
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
EXPORT_EMBER_TO_XML(double)
|
||||
#endif
|
||||
}
|
@ -27,835 +27,16 @@ public:
|
||||
~EmberToXml() = default;
|
||||
EmberToXml(const EmberToXml<T>& e) = delete;
|
||||
|
||||
/// <summary>
|
||||
/// Save the ember to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Full path and filename</param>
|
||||
/// <param name="ember">The ember to save</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <param name="append">If true, append to the file if it already exists, else create a new file.</param>
|
||||
/// <param name="start">Whether a new file is to be started</param>
|
||||
/// <param name="finish">Whether an existing file is to be ended</param>
|
||||
/// <returns>True if successful, else false</returns>
|
||||
bool Save(const string& filename, Ember<T>& ember, size_t printEditDepth, bool doEdits, bool hexPalette, bool append = false, bool start = false, bool finish = false)
|
||||
{
|
||||
vector<Ember<T>> vec;
|
||||
vec.push_back(ember);
|
||||
return Save(filename, vec, printEditDepth, doEdits, hexPalette, append, start, finish);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save a container of embers to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Full path and filename</param>
|
||||
/// <param name="embers">The container of embers to save</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <param name="append">If true, append to the file if it already exists, else create a new file.</param>
|
||||
/// <param name="start">Whether a new file is to be started</param>
|
||||
/// <param name="finish">Whether an existing file is to be ended</param>
|
||||
/// <returns>True if successful, else false</returns>
|
||||
bool Save(const string& filename, Ember<T>& ember, size_t printEditDepth, bool doEdits, bool hexPalette, bool append = false, bool start = false, bool finish = false);
|
||||
template <typename Alloc, template <typename, typename> class C>
|
||||
bool Save(const string& filename, C<Ember<T>, Alloc>& embers, size_t printEditDepth, bool doEdits, bool hexPalette, bool append = false, bool start = false, bool finish = false)
|
||||
{
|
||||
bool b = false;
|
||||
bool hasTimes = 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())
|
||||
{
|
||||
auto prev = embers.begin();
|
||||
|
||||
//Always ensure times make sense.
|
||||
for (auto& ember : embers)
|
||||
ember.m_Time = t++;
|
||||
|
||||
if ((append && start) || !append)
|
||||
{
|
||||
temp = "<flames>\n";
|
||||
//temp = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<flames>\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 = "</flames>\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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the Xml string representation of an ember.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to create the Xml with</param>
|
||||
/// <param name="extraAttributes">If true, add extra attributes, else don't</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <returns>The Xml string representation of the passed in ember</returns>
|
||||
string ToString(Ember<T>& ember, const string& extraAttributes, size_t printEditDepth, bool doEdits, bool hexPalette = true)
|
||||
{
|
||||
size_t i, j;
|
||||
string s;
|
||||
ostringstream os;
|
||||
vector<Variation<T>*> variations;
|
||||
os << "<flame version=\"EMBER-" << EmberVersion() << "\" time=\"" << ember.m_Time << "\"";
|
||||
|
||||
if (!ember.m_Name.empty())
|
||||
os << " name=\"" << ember.m_Name << "\"";
|
||||
|
||||
os << " size=\"" << ember.m_FinalRasW << " " << ember.m_FinalRasH << "\"";
|
||||
os << " center=\"" << ember.m_CenterX << " " << ember.m_CenterY << "\"";
|
||||
os << " scale=\"" << ember.m_PixelsPerUnit << "\"";
|
||||
|
||||
if (ember.m_Zoom != 0)
|
||||
os << " zoom=\"" << ember.m_Zoom << "\"";
|
||||
|
||||
os << " rotate=\"" << ember.m_Rotate << "\"";
|
||||
os << " supersample=\"" << std::max<size_t>(1, ember.m_Supersample) << "\"";
|
||||
os << " filter=\"" << ember.m_SpatialFilterRadius << "\"";
|
||||
os << " filter_shape=\"" << ToLower(SpatialFilterCreator<T>::ToString(ember.m_SpatialFilterType)) << "\"";
|
||||
os << " temporal_filter_type=\"" << ToLower(TemporalFilterCreator<T>::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 << " 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 << " 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 << "\"";
|
||||
|
||||
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 << ember.m_Curves.m_Weights[ci][cj] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
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 << " <symmetry kind=\"" << ember.m_Symmetry << "\"/>\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 << " <palette count=\"256\" format=\"RGB\">\n";
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
os << " ";
|
||||
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
size_t idx = 8 * i + j;
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(ember.m_Palette[idx][0] * 255));
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(ember.m_Palette[idx][1] * 255));
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(ember.m_Palette[idx][2] * 255));
|
||||
}
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
os << " </palette>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
double r = ember.m_Palette[i][0] * 255;
|
||||
double g = ember.m_Palette[i][1] * 255;
|
||||
double b = ember.m_Palette[i][2] * 255;
|
||||
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 << "<color index=\"" << i << "\" rgb=\"" << std::fixed << std::setprecision(2) << r << " " << g << " " << b << "\"/>";
|
||||
else
|
||||
os << " <color index=\"" << i << "\" rgba=\"" << std::fixed << std::setprecision(2) << r << " " << g << " " << b << " " << a << "\"/>";
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (doEdits && ember.m_Edits)
|
||||
os << ToString(xmlDocGetRootElement(ember.m_Edits), 1, true, printEditDepth);
|
||||
|
||||
os << "</flame>\n";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="parent0">The first parent, optionally nullptr.</param>
|
||||
/// <param name="parent1">The second parent, optionally nullptr.</param>
|
||||
/// <param name="action">The action that was taken to create the new ember</param>
|
||||
/// <param name="nick">The nickname of the author</param>
|
||||
/// <param name="url">The Url of the author</param>
|
||||
/// <param name="id">The id of the author</param>
|
||||
/// <param name="comment">The comment to include</param>
|
||||
/// <param name="sheepGen">The sheep generation used if > 0. Default: 0.</param>
|
||||
/// <param name="sheepId">The sheep id used if > 0. Default: 0.</param>
|
||||
/// <returns></returns>
|
||||
xmlDocPtr CreateNewEditdoc(Ember<T>* parent0, Ember<T>* parent1, const string& action, const string& nick, const string& url, const string& id, const string& comment, intmax_t sheepGen = 0, intmax_t sheepId = 0)
|
||||
{
|
||||
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 << "<comm>" << comment << "</comm>";
|
||||
s = os.str();
|
||||
commentDoc = xmlReadMemory(s.c_str(), int(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;
|
||||
}
|
||||
bool Save(const string& filename, C<Ember<T>, Alloc>& embers, size_t printEditDepth, bool doEdits, bool hexPalette, bool append = false, bool start = false, bool finish = false);
|
||||
string ToString(Ember<T>& ember, const string& extraAttributes, size_t printEditDepth, bool doEdits, bool hexPalette = true);
|
||||
xmlDocPtr CreateNewEditdoc(Ember<T>* parent0, Ember<T>* parent1, const string& action, const string& nick, const string& url, const string& id, const string& comment, intmax_t sheepGen = 0, intmax_t sheepId = 0);
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Return the Xml string representation of an xform.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform to create the Xml with</param>
|
||||
/// <param name="xformCount">The number of non-final xforms in the ember to which this xform belongs. Used for xaos.</param>
|
||||
/// <param name="isFinal">True if the xform is the final xform in the ember, else false.</param>
|
||||
/// <param name="doMotion">If true, include motion elements in the Xml string, else omit.</param>
|
||||
/// <returns>The Xml string representation of the passed in xform</returns>
|
||||
string ToString(Xform<T>& xform, size_t xformCount, bool isFinal, bool doMotion)
|
||||
{
|
||||
size_t i, j;
|
||||
ostringstream os;
|
||||
|
||||
if (doMotion)
|
||||
{
|
||||
os << " <motion motion_frequency=\"" << xform.m_MotionFreq << "\" ";
|
||||
|
||||
if (xform.m_MotionFunc == eMotion::MOTION_SIN)
|
||||
os << "motion_function=\"sin\" ";
|
||||
else if (xform.m_MotionFunc == eMotion::MOTION_TRIANGLE)
|
||||
os << "motion_function=\"triangle\" ";
|
||||
else if (xform.m_MotionFunc == eMotion::MOTION_HILL)
|
||||
os << "motion_function=\"hill\" ";
|
||||
else if (xform.m_MotionFunc == eMotion::MOTION_SAW)
|
||||
os << "motion_function=\"saw\" ";
|
||||
|
||||
if (xform.m_MotionOffset != 0)
|
||||
os << "motion_offset=\"" << xform.m_MotionOffset << "\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isFinal)
|
||||
os << " <finalxform ";
|
||||
else
|
||||
os << " <xform weight=\"" << xform.m_Weight << "\" ";
|
||||
}
|
||||
|
||||
if (!doMotion || xform.m_ColorX != EMPTYFIELD) os << "color=\"" << xform.m_ColorX << "\" ";
|
||||
|
||||
//if (!doMotion || xform.m_ColorY != EMPTYFIELD) os << "color=\"" << xform.m_ColorX << " " << xform.m_ColorY << "\" ";
|
||||
if (!doMotion || xform.m_DirectColor != EMPTYFIELD) os << "var_color=\"" << xform.m_DirectColor << "\" ";
|
||||
|
||||
if (!doMotion || xform.m_ColorSpeed != EMPTYFIELD) os << "color_speed=\"" << xform.m_ColorSpeed << "\" ";
|
||||
|
||||
//os << "symmetry=\"" << fabs(xform.m_ColorSpeed - 1) * 2 << "\" ";//Legacy support.
|
||||
|
||||
if (!doMotion)
|
||||
{
|
||||
string s = xform.m_Name;
|
||||
std::replace(s.begin(), s.end(), ' ', '_');
|
||||
os << "name=\"" << s << "\" ";//Flam3 didn't do this, but Apo does.
|
||||
|
||||
if (!isFinal)
|
||||
os << "animate=\"" << xform.m_Animate << "\" ";
|
||||
}
|
||||
|
||||
//Variation writing order differs slightly from the original to make it a bit more readable.
|
||||
//The original wrote out all of the variation names and weights. Then wrote out the parameters for
|
||||
//the parametric variations. Here, write out the params immediately after each parametric variation
|
||||
//so they are more closely grouped with the variation they apply to, rather than being all grouped at the end.
|
||||
for (i = 0; i < xform.TotalVariationCount(); i++)
|
||||
{
|
||||
Variation<T>* var = xform.GetVariation(i);
|
||||
ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(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 << " </finalxform>\n";
|
||||
else
|
||||
os << " </xform>\n";
|
||||
}
|
||||
else
|
||||
os << "/>\n";
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an edit node Xml string.
|
||||
/// </summary>
|
||||
/// <param name="editNode">The edit node to get the string for</param>
|
||||
/// <param name="tabs">How many tabs to use</param>
|
||||
/// <param name="formatting">If true, include newlines and tabs, else don't.</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <returns>The edit node Xml string</returns>
|
||||
string 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<void*>(const_cast<char*>(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<char*>(xmlNodeGetContent(editNode)));
|
||||
os << Trim(s);
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a FlameMotion element to an xml string
|
||||
/// </summary>
|
||||
/// <param name="motion">The FlameMotion object to convert to XML</param>
|
||||
string ToString(const EmberMotion<T>& motion)
|
||||
{
|
||||
ostringstream os;
|
||||
os << "<flame_motion motion_frequency=\"" << motion.m_MotionFreq << "\" ";
|
||||
|
||||
if (motion.m_MotionOffset > 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_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_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();
|
||||
}
|
||||
|
||||
void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename)
|
||||
{
|
||||
if (filename.find_first_of('&') != std::string::npos)
|
||||
{
|
||||
string filenameWithoutAmpersands = filename;
|
||||
FindAndReplace<string>(filenameWithoutAmpersands, "&", "&");
|
||||
xmlNewProp(node, XC("filename"), XC(filenameWithoutAmpersands.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlNewProp(node, XC("filename"), XC(filename.c_str()));
|
||||
}
|
||||
}
|
||||
string ToString(Xform<T>& xform, size_t xformCount, bool isFinal, bool doMotion);
|
||||
string ToString(xmlNodePtr editNode, size_t tabs, bool formatting, size_t printEditDepth);
|
||||
string ToString(const EmberMotion<T>& motion);
|
||||
void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename);
|
||||
};
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ namespace EmberNs
|
||||
/// The palette stores a set of 256 colors which are what get accumulated to the histogram
|
||||
/// for each iteration. The colors come from either the main palette Xml file or directly
|
||||
/// from the ember parameter file. Either way, they come in as 0-255 and get normalized to 0-1.
|
||||
/// The palette may have also come from a palette editor where the user specifies key colors, then
|
||||
/// those are interpolated to make a smooth palette. In that case, the m_SourceColors map will
|
||||
/// be populated.
|
||||
/// In the future, 2D palette support might be added in which case this class will have to be modified.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
@ -90,13 +93,27 @@ public:
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
m_Entries[i].a = T(palette15[i * 4 + 0]);
|
||||
m_Entries[i].r = T(palette15[i * 4 + 1]);
|
||||
m_Entries[i].g = T(palette15[i * 4 + 2]);
|
||||
m_Entries[i].b = T(palette15[i * 4 + 3]);
|
||||
m_Entries[i].r = T(palette15[i * 4 + 1]) / T(255);
|
||||
m_Entries[i].g = T(palette15[i * 4 + 2]) / T(255);
|
||||
m_Entries[i].b = T(palette15[i * 4 + 3]) / T(255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which takes the vector of colors as well as the source colors which were
|
||||
/// used to create it in a palette editor. The burden is on the user to not let the two get out of sync.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the palette</param>
|
||||
/// <param name="entries">A vector of color entries</param>
|
||||
/// <param name="sourceColors">A map of colors which was used to create entries in a palette editor</param>
|
||||
Palette(const string& name, vector<v4T>& entries, map<T, v4T>& sourceColors)
|
||||
{
|
||||
m_Name = name;
|
||||
m_Entries = entries;
|
||||
m_SourceColors = sourceColors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
@ -145,6 +162,11 @@ public:
|
||||
m_Name = palette.m_Name;
|
||||
m_Filename = palette.m_Filename;
|
||||
CopyCont(m_Entries, palette.m_Entries);
|
||||
m_SourceColors.clear();
|
||||
|
||||
for (auto& kv : palette.m_SourceColors)
|
||||
m_SourceColors[T(kv.first)] = v4T(kv.second);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -183,6 +205,13 @@ public:
|
||||
/// <returns>The size of the color entries vector</returns>
|
||||
size_t Size() { return m_Entries.size(); }
|
||||
|
||||
/// <summary>
|
||||
/// The size of the source color entries vector which was used to create the palette.
|
||||
/// Note this will only be non-zero if this palette was created in the palette editor.
|
||||
/// </summary>
|
||||
/// <returns>The size of the source colors map</returns>
|
||||
size_t SourceColorSize() { return m_SourceColors.size(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set all colors to either black or white, including the alpha channel.
|
||||
/// </summary>
|
||||
@ -585,5 +614,6 @@ public:
|
||||
string m_Name = "-";//Name of this palette.
|
||||
shared_ptr<string> m_Filename;//Name of the parent file this palette came from, can be empty.
|
||||
vector<v4T> m_Entries;
|
||||
map<T, v4T> m_SourceColors;
|
||||
};
|
||||
}
|
||||
|
779
Source/Ember/PaletteList.cpp
Normal file
779
Source/Ember/PaletteList.cpp
Normal file
@ -0,0 +1,779 @@
|
||||
#include "EmberPch.h"
|
||||
#include "PaletteList.h"
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Empty constructor which initializes the palette map with the default palette file.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
PaletteList<T>::PaletteList()
|
||||
{
|
||||
//Add(string(m_DefaultFilename));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor which saves any modifiable palettes to file, just in case they were modified.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
PaletteList<T>::~PaletteList()
|
||||
{
|
||||
for (auto& palFile : s_Palettes)
|
||||
{
|
||||
if (IsModifiable(palFile.first))
|
||||
Save(palFile.first);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new palette file with the given name and vector of palettes, and save it.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the file to add</param>
|
||||
/// <param name="palettes">The list of palettes which comprise the file</param>
|
||||
/// <returns>True if the file did not exist, was successfully added and saved, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::AddPaletteFile(const string& filename, const vector<Palette<T>>& palettes)
|
||||
{
|
||||
if (!GetPaletteListByFullPath(filename))
|
||||
{
|
||||
auto item = s_Palettes.insert(make_pair(filename, palettes));
|
||||
Save(filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an new empty palette file with the given name with a single modifiable palette in it.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the file to add</param>
|
||||
/// <param name="palettes">The list of palettes which comprise the file</param>
|
||||
/// <returns>True if the file did not exist, was successfully added and saved, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::AddEmptyPaletteFile(const string& filename)
|
||||
{
|
||||
if (!GetPaletteListByFullPath(filename))
|
||||
{
|
||||
auto item = s_Palettes.insert(make_pair(filename, vector<Palette<T>>()));
|
||||
Palette<T> p;
|
||||
p.m_Index = 0;
|
||||
p.m_Name = "empty-default";
|
||||
p.m_Filename = make_shared<string>(filename);
|
||||
p.m_SourceColors = map<T, v4T>
|
||||
{
|
||||
{ T(0), v4T(T(0), T(0), T(0), T(1)) },
|
||||
{ T(1), v4T(T(0), T(0), T(0), T(1)) }
|
||||
};
|
||||
item.first->second.push_back(p);
|
||||
Save(filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new palette to an existing palette file and save the file.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the existing palette file to add</param>
|
||||
/// <param name="palette">The new palette to add to the file</param>
|
||||
/// <returns>True if the palette file existed, the palette was added, and the file was successfully saved, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::AddPaletteToFile(const string& filename, const Palette<T>& palette)
|
||||
{
|
||||
if (auto p = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
p->push_back(palette);
|
||||
p->back().m_Index = int(p->size()) - 1;
|
||||
Save(filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace an existing palette in a palette file with a new one and save the file.
|
||||
/// The match is done based on palette name, so if there are duplicate names in
|
||||
/// the file, only the first one will be replaced.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the existing palette file to replace a palette in</param>
|
||||
/// <param name="palette">The new palette to use to replace an existing one in the file</param>
|
||||
/// <returns>True if the palette file existed, the palette was replaced, and the file was successfully saved, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::Replace(const string& filename, const Palette<T>& palette)
|
||||
{
|
||||
if (auto p = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
for (auto& pal : *p)
|
||||
{
|
||||
if (pal.m_Name == palette.m_Name)
|
||||
{
|
||||
auto index = pal.m_Index;
|
||||
pal = palette;
|
||||
pal.m_Index = index;
|
||||
Save(filename);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace an existing palette in a palette file with a new one and save the file.
|
||||
/// The match is done based on the passed in index.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the existing palette file to replace a palette in</param>
|
||||
/// <param name="palette">The new palette to use to replace an existing one in the file</param>
|
||||
/// <param name="index">The 0-based index of the palette to replace</param>
|
||||
/// <returns>True if the palette file existed, the palette was replaced, and the file was successfully saved, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::Replace(const string& filename, const Palette<T>& palette, int index)
|
||||
{
|
||||
if (auto p = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
if (index < p->size())
|
||||
{
|
||||
(*p)[index] = palette;
|
||||
(*p)[index].m_Index = index;
|
||||
Save(filename);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete an existing palette from a palette file.
|
||||
/// The match is done based on the passed in index.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the existing palette file to delete a palette from</param>
|
||||
/// <param name="index">The 0-based index of the palette to delete</param>
|
||||
/// <returns>True if the palette file existed, the palette was deleted, and the file was successfully saved, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::Delete(const string& filename, int index)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (auto p = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
if (index < p->size())
|
||||
{
|
||||
p->erase(p->begin() + index);
|
||||
|
||||
for (auto& pal : *p)
|
||||
pal.m_Index = i++;
|
||||
|
||||
Save(filename);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Xml palette file into memory.
|
||||
/// This must be called before any palette file usage.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the file to read</param>
|
||||
/// <param name="force">If true, override the initialization state and force a read, else observe the initialization state.</param>
|
||||
/// <returns>Whether anything was read</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::Add(const string& filename, bool force)
|
||||
{
|
||||
bool added = true;
|
||||
bool contains = GetPaletteListByFullPathOrFilename(filename) != nullptr;
|
||||
auto filenameonly = GetFilename(filename);
|
||||
|
||||
if (contains && !force)//Don't allow any palettes with the same name, even if they reside in different paths.
|
||||
return false;
|
||||
|
||||
auto palettes = s_Palettes.insert(make_pair(filename, vector<Palette<T>>()));
|
||||
|
||||
if (force || palettes.second)
|
||||
{
|
||||
string buf;
|
||||
const char* loc = __FUNCTION__;
|
||||
|
||||
if (ReadFile(filename.c_str(), buf))
|
||||
{
|
||||
auto lower = ToLower(filename);
|
||||
auto pfilename = shared_ptr<string>(new string(filename));
|
||||
|
||||
if (EndsWith(lower, ".xml"))
|
||||
{
|
||||
xmlDocPtr doc = xmlReadMemory(static_cast<const char*>(buf.data()), int(buf.size()), filename.c_str(), nullptr, XML_PARSE_NONET);
|
||||
|
||||
if (doc)
|
||||
{
|
||||
auto rootNode = xmlDocGetRootElement(doc);
|
||||
palettes.first->second.clear();
|
||||
palettes.first->second.reserve(buf.size() / 2048);//Roughly what it takes per palette.
|
||||
ParsePalettes(rootNode, pfilename, palettes.first->second);
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
if (palettes.first->second.empty())
|
||||
{
|
||||
added = false;//Reading failed, likely not a valid palette file.
|
||||
s_Palettes.erase(filename);
|
||||
AddToReport(string(loc) + " : Couldn't parse xml doc");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
added = false;
|
||||
s_Palettes.erase(filename);
|
||||
AddToReport(string(loc) + " : Couldn't load xml doc");
|
||||
}
|
||||
}
|
||||
else if (EndsWith(lower, ".ugr") || EndsWith(lower, ".gradient") || EndsWith(lower, ".gradients"))
|
||||
{
|
||||
if (!ParsePalettes(buf, pfilename, palettes.first->second))
|
||||
{
|
||||
added = false;
|
||||
s_Palettes.erase(filename);
|
||||
AddToReport(string(loc) + " : Couldn't read palette file " + filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
added = false;
|
||||
s_Palettes.erase(filename);
|
||||
AddToReport(string(loc) + " : Couldn't read palette file " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette at a random index in a random file in the map.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to a random palette in a random file if successful, else nullptr.</returns>
|
||||
template <typename T>
|
||||
Palette<T>* PaletteList<T>::GetRandomPalette()
|
||||
{
|
||||
auto p = s_Palettes.begin();
|
||||
size_t i = 0, paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % Size();
|
||||
|
||||
//Move p forward i elements.
|
||||
while (i < paletteFileIndex && p != s_Palettes.end())
|
||||
{
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (i < Size())
|
||||
{
|
||||
size_t paletteIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % p->second.size();
|
||||
|
||||
if (paletteIndex < p->second.size())
|
||||
return &p->second[paletteIndex];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette at a specified index in the specified file in the map.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename of the palette to retrieve</param>
|
||||
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
|
||||
/// <returns>A pointer to the requested palette if the index was in range, else nullptr.</returns>
|
||||
template <typename T>
|
||||
Palette<T>* PaletteList<T>::GetPaletteByFilename(const string& filename, size_t i)
|
||||
{
|
||||
if (auto palettes = GetPaletteListByFilename(filename))
|
||||
if (i < palettes->size())
|
||||
return &(*palettes)[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette at a specified index in the specified file in the map.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path and filename of the palette to retrieve</param>
|
||||
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
|
||||
/// <returns>A pointer to the requested palette if the index was in range, else nullptr.</returns>
|
||||
template <typename T>
|
||||
Palette<T>* PaletteList<T>::GetPaletteByFullPath(const string& filename, size_t i)
|
||||
{
|
||||
if (auto palettes = GetPaletteListByFullPath(filename))
|
||||
if (i < palettes->size())
|
||||
return &(*palettes)[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to a palette with a specified name in the specified full path and filename in the map.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename of the palette to retrieve</param>
|
||||
/// <param name="name">The name of the palette to retrieve</param>
|
||||
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
||||
template <typename T>
|
||||
Palette<T>* PaletteList<T>::GetPaletteByName(const string& filename, const string& name)
|
||||
{
|
||||
if (auto palettes = GetPaletteListByFullPathOrFilename(filename))
|
||||
for (auto& palette : *palettes)
|
||||
if (palette.m_Name == name)
|
||||
return &palette;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette file with the specified filename in the map.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename of the palette to retrieve</param>
|
||||
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
||||
template <typename T>
|
||||
vector<Palette<T>>* PaletteList<T>::GetPaletteListByFilename(const string& filename)
|
||||
{
|
||||
auto filenameonly = GetFilename(filename);
|
||||
|
||||
for (auto& palettes : s_Palettes)
|
||||
if (GetFilename(palettes.first) == filenameonly)
|
||||
return &palettes.second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette file with the specified full path and filename in the map.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path and filename of the palette to retrieve</param>
|
||||
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
||||
template <typename T>
|
||||
vector<Palette<T>>* PaletteList<T>::GetPaletteListByFullPath(const string& filename)
|
||||
{
|
||||
auto palettes = s_Palettes.find(filename);
|
||||
|
||||
if (palettes != s_Palettes.end() && !palettes->second.empty())
|
||||
return &palettes->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette file with the specified full path and filename in the map.
|
||||
/// If that does not work, try getting it with the filename alone.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path and filename or just the filename of the palette to retrieve</param>
|
||||
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
||||
template <typename T>
|
||||
vector<Palette<T>>* PaletteList<T>::GetPaletteListByFullPathOrFilename(const string& filename)
|
||||
{
|
||||
auto p = GetPaletteListByFullPath(filename);
|
||||
|
||||
if (!p)
|
||||
p = GetPaletteListByFilename(filename);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get full path and filename of the pallete with the specified filename
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename only of the palette to retrieve</param>
|
||||
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
||||
template <typename T>
|
||||
string PaletteList<T>::GetFullPathFromFilename(const string& filename)
|
||||
{
|
||||
auto filenameonly = GetFilename(filename);
|
||||
|
||||
for (auto& palettes : s_Palettes)
|
||||
if (GetFilename(palettes.first) == filenameonly)
|
||||
return palettes.first;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a copy of the palette at a specified index in the specified file in the map
|
||||
/// with its hue adjusted by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename of the palette to retrieve</param>
|
||||
/// <param name="i">The index of the palette to read.</param>
|
||||
/// <param name="hue">The hue adjustment to apply</param>
|
||||
/// <param name="palette">The palette to store the output</param>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette<T>& palette)
|
||||
{
|
||||
if (auto unadjustedPal = GetPaletteByFullPath(filename, i))
|
||||
{
|
||||
unadjustedPal->MakeHueAdjustedPalette(palette, hue);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the palette list and reset the initialization state.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void PaletteList<T>::Clear()
|
||||
{
|
||||
s_Palettes.clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of the palettes map.
|
||||
/// This will be the number of files read.
|
||||
/// </summary>
|
||||
/// <returns>The size of the palettes map</returns>
|
||||
template <typename T>
|
||||
size_t PaletteList<T>::Size() { return s_Palettes.size(); }
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of specified palette vector in the palettes map.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the palette in the map to retrieve</param>
|
||||
/// <returns>The size of the palette vector at the specified index in the palettes map</returns>
|
||||
template <typename T>
|
||||
size_t PaletteList<T>::Size(size_t index)
|
||||
{
|
||||
size_t i = 0;
|
||||
auto p = s_Palettes.begin();
|
||||
|
||||
while (i < index && p != s_Palettes.end())
|
||||
{
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
|
||||
return p->second.size();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of specified palette vector in the palettes map.
|
||||
/// </summary>
|
||||
/// <param name="s">The filename of the palette in the map to retrieve</param>
|
||||
/// <returns>The size of the palette vector at the specified index in the palettes map</returns>
|
||||
template <typename T>
|
||||
size_t PaletteList<T>::Size(const string& s)
|
||||
{
|
||||
return s_Palettes[s].size();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of specified palette in the palettes map.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the palette in the map to retrieve</param>
|
||||
/// <returns>The name of the palette vector at the specified index in the palettes map</returns>
|
||||
template <typename T>
|
||||
const string& PaletteList<T>::Name(size_t index)
|
||||
{
|
||||
size_t i = 0;
|
||||
auto p = s_Palettes.begin();
|
||||
|
||||
while (i < index && p != s_Palettes.end())
|
||||
{
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
|
||||
return p->first;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether at least one palette in the passed in palette file is modifiable,
|
||||
/// meaning whether the source colors have at least one element in them.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the existing palette file to search for a modifiable palette in</param>
|
||||
/// <returns>True if at least one palette in the file was modifiable, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::IsModifiable(const string& filename)
|
||||
{
|
||||
if (auto palFile = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
for (auto& pal : *palFile)
|
||||
if (!pal.m_SourceColors.empty())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a const ref to the underlying static palette structure.
|
||||
/// </summary>
|
||||
/// <returns>s_Palettes</returns>
|
||||
template <typename T>
|
||||
const map<string, vector<Palette<T>>>& PaletteList<T>::Palettes() const
|
||||
{
|
||||
return s_Palettes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves an existing file to disk.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the existing palette file to save</param>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::Save(const string& filename)
|
||||
{
|
||||
auto fullpath = GetFullPathFromFilename(filename);
|
||||
|
||||
try
|
||||
{
|
||||
size_t index = 0;
|
||||
ostringstream os;
|
||||
|
||||
if (auto palFile = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
ofstream f(fullpath);
|
||||
os << "<palettes>\n";
|
||||
|
||||
if (f.is_open())
|
||||
{
|
||||
for (auto& pal : *palFile)
|
||||
{
|
||||
os << "<palette number=\"" << index++ << "\" name=\"" << pal.m_Name << "\"";
|
||||
|
||||
if (!pal.m_SourceColors.empty())
|
||||
{
|
||||
os << " source_colors=\"";
|
||||
|
||||
for (auto& sc : pal.m_SourceColors)//Need to clamp these each from 0 to 1. Use our custom clamp funcs.//TODO
|
||||
os << sc.first << "," << sc.second.r << "," << sc.second.g << "," << sc.second.b << " ";
|
||||
|
||||
os << "\"";
|
||||
}
|
||||
|
||||
os << " data=\"";
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
size_t idx = 8 * i + j;
|
||||
os << "00";
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(pal[idx][0] * 255));
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(pal[idx][1] * 255));
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(pal[idx][2] * 255));
|
||||
}
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
os << "\"/>\n";
|
||||
}
|
||||
}
|
||||
|
||||
os << "</palettes>";
|
||||
string s = os.str();
|
||||
f.write(s.c_str(), s.size());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
cout << "Error: Writing palette file " << fullpath << " failed: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cout << "Error: Writing palette file " << fullpath << " failed.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an Xml node for all palettes present and stores them in the passed in palette vector.
|
||||
/// Note that although the Xml color values are expected to be 0-255, they are converted and
|
||||
/// stored as normalized colors, with values from 0-1.
|
||||
/// </summary>
|
||||
/// <param name="node">The parent note of all palettes in the Xml file.</param>
|
||||
/// <param name="filename">The name of the Xml file.</param>
|
||||
/// <param name="palettes">The vector to store the parsed palettes associated with this file in.</param>
|
||||
template <typename T>
|
||||
void PaletteList<T>::ParsePalettes(xmlNode* node, const shared_ptr<string>& filename, vector<Palette<T>>& palettes)
|
||||
{
|
||||
char* val;
|
||||
xmlAttrPtr attr;
|
||||
int index = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
if (node->type == XML_ELEMENT_NODE && !Compare(node->name, "palette"))
|
||||
{
|
||||
attr = node->properties;
|
||||
Palette<T> palette;
|
||||
|
||||
while (attr)
|
||||
{
|
||||
val = reinterpret_cast<char*>(xmlGetProp(node, attr->name));
|
||||
|
||||
if (!Compare(attr->name, "data"))
|
||||
{
|
||||
string s1, s;
|
||||
size_t tmp, colorCount = 0;
|
||||
stringstream ss, temp(val); ss >> std::hex;
|
||||
s.reserve(2048);
|
||||
|
||||
while (temp >> s1)
|
||||
s += s1;
|
||||
|
||||
auto length = s.size();
|
||||
|
||||
for (size_t strIndex = 0; strIndex < length;)
|
||||
{
|
||||
strIndex += 2;//Skip past the 00 at the beginning of each RGB.
|
||||
|
||||
for (glm::length_t i = 0; i < 3 && colorCount < palette.Size(); i++)
|
||||
{
|
||||
const char tmpStr[3] = { s[strIndex++], s[strIndex++], 0 };//Read out and convert the string two characters at a time.
|
||||
ss.clear();//Reset and fill the string stream.
|
||||
ss.str(tmpStr);
|
||||
ss >> tmp;//Do the conversion.
|
||||
palette.m_Entries[colorCount][i] = T(tmp) / T(255);//Hex palette is [0..255], convert to [0..1].
|
||||
}
|
||||
|
||||
colorCount++;
|
||||
}
|
||||
}
|
||||
else if (!Compare(attr->name, "source_colors"))
|
||||
{
|
||||
string s(val);
|
||||
auto vec1 = Split(s, ' ');
|
||||
|
||||
for (auto& v : vec1)
|
||||
{
|
||||
auto vec2 = Split(v, ',');
|
||||
|
||||
if (vec2.size() == 4)
|
||||
{
|
||||
float d1 = Clamp(std::stof(vec2[0]), 0.0f, 1.0f);
|
||||
palette.m_SourceColors[d1] = v4F(Clamp(std::stof(vec2[1]), 0.0f, 1.0f),
|
||||
Clamp(std::stof(vec2[2]), 0.0f, 1.0f),
|
||||
Clamp(std::stof(vec2[3]), 0.0f, 1.0f), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!Compare(attr->name, "name"))
|
||||
{
|
||||
palette.m_Name = string(val);
|
||||
}
|
||||
|
||||
xmlFree(val);
|
||||
attr = attr->next;
|
||||
}
|
||||
|
||||
palette.m_Index = index++;
|
||||
palette.m_Filename = filename;
|
||||
palettes.push_back(palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParsePalettes(node->children, filename, palettes);
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a gradient file for all palettes present and stores them in the passed in palette vector.
|
||||
/// Note that although the Xml color values are expected to be 0-255, they are converted and
|
||||
/// stored as normalized colors, with values from 0-1.
|
||||
/// This format is from Ultra Fractal and Apophysis.
|
||||
/// </summary>
|
||||
/// <param name="buf">The data to parse.</param>
|
||||
/// <param name="filename">The name of the gradient file.</param>
|
||||
/// <param name="palettes">The vector to store the parsed palettes associated with this file in.</param>
|
||||
/// <returns>True if at least one palette is read, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::ParsePalettes(const string& buf, const shared_ptr<string>& filename, vector<Palette<T>>& palettes)
|
||||
{
|
||||
int paletteIndex = 0;
|
||||
size_t index = 0;
|
||||
string line;
|
||||
string name;
|
||||
bool reading = false;
|
||||
bool found = false;
|
||||
istringstream iss(buf);
|
||||
vector<string> splitVec;
|
||||
const char leftBrace = '{';
|
||||
const char rightBrace = '}';
|
||||
const string titleStr = "title=";
|
||||
const string indexStr = "index=";
|
||||
const string colorStr = "color=";
|
||||
const string titleDelStr = " =\"";
|
||||
const string colorDelStr = " =";
|
||||
Palette<T> palette;
|
||||
Color<T> col;
|
||||
palettes.clear();
|
||||
|
||||
while (std::getline(iss, line))
|
||||
{
|
||||
if (!reading && Contains(line, leftBrace))
|
||||
{
|
||||
reading = true;
|
||||
}
|
||||
else if (Contains(line, rightBrace))
|
||||
{
|
||||
if (found)
|
||||
palettes.push_back(palette);
|
||||
|
||||
reading = false;
|
||||
found = false;
|
||||
}
|
||||
|
||||
if (reading)
|
||||
{
|
||||
if (Find(line, titleStr))
|
||||
{
|
||||
splitVec = Split(line, titleDelStr, true);
|
||||
|
||||
if (splitVec.size() > 2)
|
||||
name = splitVec[1];
|
||||
}
|
||||
else if (Find(line, indexStr) && Find(line, colorStr))
|
||||
{
|
||||
if (!found)
|
||||
{
|
||||
index = 0;
|
||||
found = true;
|
||||
palette.Clear();
|
||||
palette.m_Index = paletteIndex++;
|
||||
palette.m_Name = name;
|
||||
palette.m_Filename = filename;
|
||||
}
|
||||
|
||||
splitVec = Split(line, colorDelStr, true);
|
||||
|
||||
if (splitVec.size() > 3 && index < 256)
|
||||
{
|
||||
int val = std::stoi(splitVec[3]);
|
||||
col.r = (val & 0xFF) / T(255);//Hex palette is [0..255], convert to [0..1].
|
||||
col.g = ((val >> 8) & 0xFF) / T(255);
|
||||
col.b = ((val >> 16) & 0xFF) / T(255);
|
||||
palette[index] = col;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !palettes.empty();
|
||||
}
|
||||
|
||||
template EMBER_API class PaletteList<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template EMBER_API class PaletteList<double>;
|
||||
#endif
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Palette.h"
|
||||
#include "Point.h"
|
||||
|
||||
/// <summary>
|
||||
/// PaletteList class.
|
||||
@ -12,7 +13,11 @@ namespace EmberNs
|
||||
/// Holds a list of palettes read from an Xml file. Since the default list from flam3-palettes.xml is fairly large at 700 palettes,
|
||||
/// the list member is kept as a static. This class derives from EmberReport in order to report any errors that occurred while reading the Xml.
|
||||
/// Note that although the Xml color values are expected to be 0-255, they are converted and stored as normalized colors, with values from 0-1.
|
||||
/// Template argument expected to be float or double.
|
||||
/// This can hold read only palettes, as well as user created and modifiable ones.
|
||||
/// The key in the map is the fully qualified path and filename to each palette file.
|
||||
/// Despite the keys being full paths, the same filename cannot be inserted twice, even if they reside
|
||||
/// in different folders. Functions are provided to retrieve palette files via filename only, or full path.
|
||||
/// Template argument should always be float (which makes the templating of this class pointless).
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API PaletteList : public EmberReport
|
||||
@ -20,294 +25,37 @@ class EMBER_API PaletteList : public EmberReport
|
||||
public:
|
||||
static const char* m_DefaultFilename;
|
||||
|
||||
/// <summary>
|
||||
/// Empty constructor which initializes the palette map with the default palette file.
|
||||
/// </summary>
|
||||
PaletteList()
|
||||
{
|
||||
Add(string(m_DefaultFilename));
|
||||
}
|
||||
|
||||
~PaletteList() = default;
|
||||
PaletteList();
|
||||
PaletteList(const PaletteList<T>& paletteList) = delete;
|
||||
|
||||
/// <summary>
|
||||
/// Read an Xml palette file into memory.
|
||||
/// This must be called before any palette file usage.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the file to read</param>
|
||||
/// <param name="force">If true, override the initialization state and force a read, else observe the initialization state.</param>
|
||||
/// <returns>Whether anything was read</returns>
|
||||
bool Add(const string& filename, bool force = false)
|
||||
{
|
||||
bool added = true;
|
||||
auto palettes = s_Palettes.insert(make_pair(filename, vector<Palette<T>>()));
|
||||
|
||||
if (force || palettes.second)
|
||||
{
|
||||
string buf;
|
||||
const char* loc = __FUNCTION__;
|
||||
|
||||
if (ReadFile(filename.c_str(), buf))
|
||||
{
|
||||
xmlDocPtr doc = xmlReadMemory(static_cast<const char*>(buf.data()), int(buf.size()), filename.c_str(), nullptr, XML_PARSE_NONET);
|
||||
|
||||
if (doc)
|
||||
{
|
||||
auto rootNode = xmlDocGetRootElement(doc);
|
||||
auto pfilename = shared_ptr<string>(new string(filename));
|
||||
palettes.first->second.clear();
|
||||
palettes.first->second.reserve(buf.size() / 2048);//Roughly what it takes per palette.
|
||||
ParsePalettes(rootNode, pfilename, palettes.first->second);
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
if (palettes.first->second.empty())
|
||||
{
|
||||
added = false;//Reading failed, likely not a valid palette file.
|
||||
s_Palettes.erase(filename);
|
||||
AddToReport(string(loc) + " : Couldn't parse xml doc");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
added = false;
|
||||
s_Palettes.erase(filename);
|
||||
AddToReport(string(loc) + " : Couldn't load xml doc");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
added = false;
|
||||
s_Palettes.erase(filename);
|
||||
AddToReport(string(loc) + " : Couldn't read palette file " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette at a random index in a random file in the map.
|
||||
/// </summary>
|
||||
Palette<T>* GetRandomPalette()
|
||||
{
|
||||
auto p = s_Palettes.begin();
|
||||
size_t i = 0, paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % Size();
|
||||
|
||||
//Move p forward i elements.
|
||||
while (i < paletteFileIndex && p != s_Palettes.end())
|
||||
{
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (i < Size())
|
||||
{
|
||||
size_t paletteIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % p->second.size();
|
||||
|
||||
if (paletteIndex < p->second.size())
|
||||
return &p->second[paletteIndex];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the palette at a specified index in the specified file in the map.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename of the palette to retrieve</param>
|
||||
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
|
||||
/// <returns>A pointer to the requested palette if the index was in range, else nullptr.</returns>
|
||||
Palette<T>* GetPalette(const string& filename, size_t i)
|
||||
{
|
||||
auto& palettes = s_Palettes[filename];
|
||||
|
||||
if (!palettes.empty() && i < palettes.size())
|
||||
return &palettes[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to a palette with a specified name in the specified file in the map.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename of the palette to retrieve</param>
|
||||
/// <param name="name">The name of the palette to retrieve</param>
|
||||
/// <returns>A pointer to the palette if found, else nullptr</returns>
|
||||
Palette<T>* GetPaletteByName(const string& filename, const string& name)
|
||||
{
|
||||
for (auto& palettes : s_Palettes)
|
||||
if (palettes.first == filename)
|
||||
for (auto& palette : palettes.second)
|
||||
if (palette.m_Name == name)
|
||||
return &palette;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a copy of the palette at a specified index in the specified file in the map
|
||||
/// with its hue adjusted by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename of the palette to retrieve</param>
|
||||
/// <param name="i">The index of the palette to read.</param>
|
||||
/// <param name="hue">The hue adjustment to apply</param>
|
||||
/// <param name="palette">The palette to store the output</param>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
bool GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette<T>& palette)
|
||||
{
|
||||
bool b = false;
|
||||
|
||||
if (Palette<T>* unadjustedPal = GetPalette(filename, i))
|
||||
{
|
||||
unadjustedPal->MakeHueAdjustedPalette(palette, hue);
|
||||
b = true;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the palette list and reset the initialization state.
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
s_Palettes.clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of the palettes map.
|
||||
/// This will be the number of files read.
|
||||
/// </summary>
|
||||
/// <returns>The size of the palettes map</returns>
|
||||
size_t Size() { return s_Palettes.size(); }
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of specified palette vector in the palettes map.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the palette in the map to retrieve</param>
|
||||
/// <returns>The size of the palette vector at the specified index in the palettes map</returns>
|
||||
size_t Size(size_t index)
|
||||
{
|
||||
size_t i = 0;
|
||||
auto p = s_Palettes.begin();
|
||||
|
||||
while (i < index && p != s_Palettes.end())
|
||||
{
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
|
||||
return p->second.size();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of specified palette vector in the palettes map.
|
||||
/// </summary>
|
||||
/// <param name="s">The filename of the palette in the map to retrieve</param>
|
||||
/// <returns>The size of the palette vector at the specified index in the palettes map</returns>
|
||||
size_t Size(const string& s)
|
||||
{
|
||||
return s_Palettes[s].size();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of specified palette in the palettes map.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the palette in the map to retrieve</param>
|
||||
/// <returns>The name of the palette vector at the specified index in the palettes map</returns>
|
||||
const string& Name(size_t index)
|
||||
{
|
||||
size_t i = 0;
|
||||
auto p = s_Palettes.begin();
|
||||
|
||||
while (i < index && p != s_Palettes.end())
|
||||
{
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
|
||||
return p->first;
|
||||
}
|
||||
~PaletteList();
|
||||
bool AddPaletteFile(const string& filename, const vector<Palette<T>>& palettes);
|
||||
bool AddEmptyPaletteFile(const string& filename);
|
||||
bool AddPaletteToFile(const string& filename, const Palette<T>& palette);
|
||||
bool Replace(const string& filename, const Palette<T>& palette);
|
||||
bool Replace(const string& filename, const Palette<T>& palette, int index);
|
||||
bool Delete(const string& filename, int index);
|
||||
bool Add(const string& filename, bool force = false);
|
||||
Palette<T>* GetRandomPalette();
|
||||
Palette<T>* GetPaletteByFilename(const string& filename, size_t i);
|
||||
Palette<T>* GetPaletteByFullPath(const string& filename, size_t i);
|
||||
Palette<T>* GetPaletteByName(const string& filename, const string& name);
|
||||
vector<Palette<T>>* GetPaletteListByFilename(const string& filename);
|
||||
vector<Palette<T>>* GetPaletteListByFullPath(const string& filename);
|
||||
vector<Palette<T>>* GetPaletteListByFullPathOrFilename(const string& filename);
|
||||
string GetFullPathFromFilename(const string& filename);
|
||||
bool GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette<T>& palette);
|
||||
void Clear();
|
||||
size_t Size();
|
||||
size_t Size(size_t index);
|
||||
size_t Size(const string& s);
|
||||
const string& Name(size_t index);
|
||||
bool IsModifiable(const string& filename);
|
||||
const map<string, vector<Palette<T>>>& Palettes() const;
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Parses an Xml node for all palettes present and stores them in the passed in palette vector.
|
||||
/// Note that although the Xml color values are expected to be 0-255, they are converted and
|
||||
/// stored as normalized colors, with values from 0-1.
|
||||
/// </summary>
|
||||
/// <param name="node">The parent note of all palettes in the Xml file.</param>
|
||||
/// <param name="filename">The name of the Xml file.</param>
|
||||
/// <param name="palettes">The vector to store the paresed palettes associated with this file in.</param>
|
||||
void ParsePalettes(xmlNode* node, const shared_ptr<string>& filename, vector<Palette<T>>& palettes)
|
||||
{
|
||||
char* val;
|
||||
const char* loc = __FUNCTION__;
|
||||
xmlAttrPtr attr;
|
||||
|
||||
while (node)
|
||||
{
|
||||
if (node->type == XML_ELEMENT_NODE && !Compare(node->name, "palette"))
|
||||
{
|
||||
attr = node->properties;
|
||||
Palette<T> palette;
|
||||
|
||||
while (attr)
|
||||
{
|
||||
val = reinterpret_cast<char*>(xmlGetProp(node, attr->name));
|
||||
|
||||
if (!Compare(attr->name, "data"))
|
||||
{
|
||||
string s1, s;
|
||||
size_t tmp, colorCount = 0, colorIndex = 0;
|
||||
stringstream ss, temp(val); ss >> std::hex;
|
||||
s.reserve(2048);
|
||||
|
||||
while (temp >> s1)
|
||||
s += s1;
|
||||
|
||||
auto length = s.size();
|
||||
|
||||
for (size_t strIndex = 0; strIndex < length;)
|
||||
{
|
||||
strIndex += 2;//Skip past the 00 at the beginning of each RGB.
|
||||
|
||||
for (glm::length_t i = 0; i < 3 && colorCount < palette.Size(); i++)
|
||||
{
|
||||
const char tmpStr[3] = { s[strIndex++], s[strIndex++], 0 };//Read out and convert the string two characters at a time.
|
||||
ss.clear();//Reset and fill the string stream.
|
||||
ss.str(tmpStr);
|
||||
ss >> tmp;//Do the conversion.
|
||||
palette.m_Entries[colorCount][i] = T(tmp) / T(255);//Hex palette is [0..255], convert to [0..1].
|
||||
}
|
||||
|
||||
colorCount++;
|
||||
}
|
||||
}
|
||||
else if (!Compare(attr->name, "number"))
|
||||
{
|
||||
palette.m_Index = atoi(val);
|
||||
}
|
||||
else if (!Compare(attr->name, "name"))
|
||||
{
|
||||
palette.m_Name = string(val);
|
||||
}
|
||||
|
||||
xmlFree(val);
|
||||
attr = attr->next;
|
||||
}
|
||||
|
||||
palette.m_Filename = filename;
|
||||
palettes.push_back(palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParsePalettes(node->children, filename, palettes);
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
bool Save(const string& filename);
|
||||
void ParsePalettes(xmlNode* node, const shared_ptr<string>& filename, vector<Palette<T>>& palettes);
|
||||
bool ParsePalettes(const string& buf, const shared_ptr<string>& filename, vector<Palette<T>>& palettes);
|
||||
|
||||
static map<string, vector<Palette<T>>> s_Palettes;//The map of filenames to vectors that store the palettes.
|
||||
};
|
||||
|
@ -60,25 +60,6 @@ bool Renderer<T, bucketT>::AssignIterator()
|
||||
template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::ComputeBounds()
|
||||
{
|
||||
//size_t maxDEFilterWidth = 0;
|
||||
//Use type T to account for negative numbers which will occur with a larger supersample and smaller filter width.
|
||||
//The final value will be of type size_t.
|
||||
//m_GutterWidth = size_t(ClampGte<T>((T(m_SpatialFilter->FinalFilterWidth()) - T(Supersample())) / 2, 0));
|
||||
//
|
||||
////Check the size of the density estimation filter.
|
||||
////If the radius of the density estimation filter is greater than the
|
||||
////gutter width, have to pad with more. Otherwise, use the same value.
|
||||
//for (auto& ember : *m_EmbersP)
|
||||
// maxDEFilterWidth = std::max<size_t>(size_t(ceil(ember.m_MaxRadDE) * m_Ember.m_Supersample), maxDEFilterWidth);
|
||||
//
|
||||
////Need an extra ss = (int)floor(m_Supersample / 2.0) of pixels so that a local iteration count for DE can be determined.//SMOULDER
|
||||
//if (maxDEFilterWidth > 0)
|
||||
// maxDEFilterWidth += size_t(Floor<T>(m_Ember.m_Supersample / T(2)));
|
||||
//To have a fully present set of pixels for the spatial filter, must
|
||||
//add the DE filter width to the spatial filter width.//SMOULDER
|
||||
//m_DensityFilterOffset = maxDEFilterWidth;
|
||||
//m_GutterWidth += m_DensityFilterOffset;
|
||||
//
|
||||
//Original did a lot of work to compute a gutter that changes size based on various parameters, which seems to be of no benefit.
|
||||
//It also prevents the renderer from only performing filtering or final accum based on a filter parameter change, since that
|
||||
//change may have changed the gutter.
|
||||
@ -720,7 +701,7 @@ EmberImageComments Renderer<T, bucketT>::ImageComments(const EmberStats& stats,
|
||||
template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::MakeDmap(T colorScalar)
|
||||
{
|
||||
m_Ember.m_Palette.template MakeDmap<bucketT>(m_Dmap, colorScalar);
|
||||
m_Ember.m_Palette.template MakeDmap<bucketT>(m_Dmap, bucketT(colorScalar));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -863,48 +844,33 @@ eRenderStatus Renderer<T, bucketT>::LogScaleDensityFilter(bool forceOutput)
|
||||
size_t endRow = m_SuperRasH;
|
||||
size_t endCol = m_SuperRasW;
|
||||
//Timing t(4);
|
||||
//if (forceOutput)//Assume interactive render, so speed up at the expense of slight quality.
|
||||
//{
|
||||
// parallel_for(startRow, endRow, [&](size_t j)
|
||||
// {
|
||||
// if (!m_Abort)
|
||||
// {
|
||||
// size_t row = j * m_SuperRasW;
|
||||
// size_t rowEnd = row + endCol;
|
||||
// VectorizedLogScale(row, rowEnd);
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
//else
|
||||
//Original didn't parallelize this, doing so gives a 50-75% speedup.
|
||||
//The value can be directly assigned, which is quicker than summing.
|
||||
parallel_for(startRow, endRow, size_t(1), [&](size_t j)
|
||||
{
|
||||
//Original didn't parallelize this, doing so gives a 50-75% speedup.
|
||||
//The value can be directly assigned, which is quicker than summing.
|
||||
parallel_for(startRow, endRow, [&](size_t j)
|
||||
size_t row = j * m_SuperRasW;
|
||||
size_t rowEnd = row + endCol;
|
||||
|
||||
if (!m_Abort)
|
||||
{
|
||||
size_t row = j * m_SuperRasW;
|
||||
size_t rowEnd = row + endCol;
|
||||
|
||||
if (!m_Abort)
|
||||
for (size_t i = row; i < rowEnd; i++)
|
||||
{
|
||||
for (size_t i = row; i < rowEnd; i++)
|
||||
//Check for visibility first before doing anything else to avoid all possible unnecessary calculations.
|
||||
if (m_HistBuckets[i].a != 0)
|
||||
{
|
||||
//Check for visibility first before doing anything else to avoid all possible unnecessary calculations.
|
||||
if (m_HistBuckets[i].a != 0)
|
||||
{
|
||||
bucketT logScale = (m_K1 * std::log(1 + m_HistBuckets[i].a * m_K2)) / m_HistBuckets[i].a;
|
||||
//Original did a temporary assignment, then *= logScale, then passed the result to bump_no_overflow().
|
||||
//Combine here into one operation for a slight speedup.
|
||||
//Vectorized version:
|
||||
bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[i]);//Vectorizer can't tell these point to different locations.
|
||||
bucketT* __restrict acc = glm::value_ptr(m_AccumulatorBuckets[i]);
|
||||
bucketT logScale = (m_K1 * std::log(1 + m_HistBuckets[i].a * m_K2)) / m_HistBuckets[i].a;
|
||||
//Original did a temporary assignment, then *= logScale, then passed the result to bump_no_overflow().
|
||||
//Combine here into one operation for a slight speedup.
|
||||
//Vectorized version:
|
||||
bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[i]);//Vectorizer can't tell these point to different locations.
|
||||
bucketT* __restrict acc = glm::value_ptr(m_AccumulatorBuckets[i]);
|
||||
|
||||
for (size_t v = 0; v < 4; v++)
|
||||
acc[v] = hist[v] * logScale;
|
||||
}
|
||||
for (size_t v = 0; v < 4; v++)
|
||||
acc[v] = hist[v] * logScale;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
//t.Toc(__FUNCTION__);
|
||||
return m_Abort ? eRenderStatus::RENDER_ABORT : eRenderStatus::RENDER_OK;
|
||||
}
|
||||
@ -930,7 +896,7 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
|
||||
intmax_t endCol = m_SuperRasW - (Supersample() - 1);
|
||||
size_t chunkSize = size_t(ceil(double(endRow - startRow) / double(threads)));
|
||||
//parallel_for scales very well, dividing the work almost perfectly among all processors.
|
||||
parallel_for(size_t(0), threads, [&] (size_t threadIndex)
|
||||
parallel_for(size_t(0), threads, size_t(1), [&] (size_t threadIndex)
|
||||
{
|
||||
size_t pixelNumber = 0;
|
||||
int localStartRow = int(std::min<size_t>(startRow + (threadIndex * chunkSize), endRow - 1));
|
||||
@ -1115,7 +1081,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
|
||||
//The original does it this way as well and it's roughly 11 times faster to do it this way than inline below with each pixel.
|
||||
if (EarlyClip())
|
||||
{
|
||||
parallel_for(size_t(0), m_SuperRasH, [&] (size_t j)
|
||||
parallel_for(size_t(0), m_SuperRasH, size_t(1), [&] (size_t j)
|
||||
{
|
||||
auto rowStart = m_AccumulatorBuckets.data() + (j * m_SuperRasW);//Pull out of inner loop for optimization.
|
||||
auto rowEnd = rowStart + m_SuperRasW;
|
||||
@ -1138,7 +1104,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
|
||||
//otherwise artifacts that resemble page tearing will occur in an interactive run. It's
|
||||
//critical to never exit this loop prematurely.
|
||||
//for (size_t j = 0; j < FinalRasH(); j++)//Keep around for debugging.
|
||||
parallel_for(size_t(0), FinalRasH(), [&](size_t j)
|
||||
parallel_for(size_t(0), FinalRasH(), size_t(1), [&](size_t j)
|
||||
{
|
||||
Color<bucketT> newBucket;
|
||||
size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRowSize();//Pull out of inner loop for optimization.
|
||||
@ -1300,13 +1266,13 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
|
||||
m_ThreadEmbers.insert(m_ThreadEmbers.begin(), m_ThreadsToUse, m_Ember);
|
||||
}
|
||||
|
||||
parallel_for(size_t(0), m_ThreadsToUse, [&] (size_t threadIndex)
|
||||
parallel_for(size_t(0), m_ThreadsToUse, size_t(1), [&] (size_t threadIndex)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
SetThreadPriority(GetCurrentThread(), int(m_Priority));
|
||||
#elif defined(__APPLE__)
|
||||
sched_param sp = {0};
|
||||
sp.sched_priority = m_Priority;
|
||||
sp.sched_priority = int(m_Priority);
|
||||
pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
|
||||
#else
|
||||
pthread_setschedprio(pthread_self(), int(m_Priority));
|
||||
@ -1441,31 +1407,31 @@ template <typename T, typename bucketT> DensityFilterBase* Renderer<T, bucketT>:
|
||||
/// Non-virtual ember wrappers, getters only.
|
||||
/// </summary>
|
||||
|
||||
template <typename T, typename bucketT> bool Renderer<T, bucketT>::XaosPresent() const { return m_Ember.XaosPresent(); }
|
||||
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::Supersample() const { return m_Ember.m_Supersample; }
|
||||
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::PaletteIndex() const { return m_Ember.PaletteIndex(); }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Time() const { return m_Ember.m_Time; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Quality() const { return m_Ember.m_Quality; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::SpatialFilterRadius() const { return m_Ember.m_SpatialFilterRadius; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsPerUnit() const { return m_Ember.m_PixelsPerUnit; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Zoom() const { return m_Ember.m_Zoom; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::CenterX() const { return m_Ember.m_CenterX; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::CenterY() const { return m_Ember.m_CenterY; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Rotate() const { return m_Ember.m_Rotate; }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::Brightness() const { return bucketT(m_Ember.m_Brightness); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::Gamma() const { return bucketT(m_Ember.m_Gamma); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::Vibrancy() const { return bucketT(m_Ember.m_Vibrancy); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::GammaThresh() const { return bucketT(m_Ember.m_GammaThresh); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::HighlightPower() const { return bucketT(m_Ember.m_HighlightPower); }
|
||||
template <typename T, typename bucketT> Color<T> Renderer<T, bucketT>::Background() const { return m_Ember.m_Background; }
|
||||
template <typename T, typename bucketT> const Xform<T>* Renderer<T, bucketT>::Xforms() const { return m_Ember.Xforms(); }
|
||||
template <typename T, typename bucketT> Xform<T>* Renderer<T, bucketT>::NonConstXforms() { return m_Ember.NonConstXforms(); }
|
||||
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::XformCount() const { return m_Ember.XformCount(); }
|
||||
template <typename T, typename bucketT> const Xform<T>* Renderer<T, bucketT>::FinalXform() const { return m_Ember.FinalXform(); }
|
||||
template <typename T, typename bucketT> Xform<T>* Renderer<T, bucketT>::NonConstFinalXform() { return m_Ember.NonConstFinalXform(); }
|
||||
template <typename T, typename bucketT> bool Renderer<T, bucketT>::UseFinalXform() const { return m_Ember.UseFinalXform(); }
|
||||
template <typename T, typename bucketT> const Palette<T>* Renderer<T, bucketT>::GetPalette() const { return &m_Ember.m_Palette; }
|
||||
template <typename T, typename bucketT> ePaletteMode Renderer<T, bucketT>::PaletteMode() const { return m_Ember.m_PaletteMode; }
|
||||
template <typename T, typename bucketT> bool Renderer<T, bucketT>::XaosPresent() const { return m_Ember.XaosPresent(); }
|
||||
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::Supersample() const { return m_Ember.m_Supersample; }
|
||||
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::PaletteIndex() const { return m_Ember.PaletteIndex(); }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Time() const { return m_Ember.m_Time; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Quality() const { return m_Ember.m_Quality; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::SpatialFilterRadius() const { return m_Ember.m_SpatialFilterRadius; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsPerUnit() const { return m_Ember.m_PixelsPerUnit; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Zoom() const { return m_Ember.m_Zoom; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::CenterX() const { return m_Ember.m_CenterX; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::CenterY() const { return m_Ember.m_CenterY; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Rotate() const { return m_Ember.m_Rotate; }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::Brightness() const { return bucketT(m_Ember.m_Brightness); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::Gamma() const { return bucketT(m_Ember.m_Gamma); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::Vibrancy() const { return bucketT(m_Ember.m_Vibrancy); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::GammaThresh() const { return bucketT(m_Ember.m_GammaThresh); }
|
||||
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::HighlightPower() const { return bucketT(m_Ember.m_HighlightPower); }
|
||||
template <typename T, typename bucketT> Color<T> Renderer<T, bucketT>::Background() const { return m_Ember.m_Background; }
|
||||
template <typename T, typename bucketT> const Xform<T>* Renderer<T, bucketT>::Xforms() const { return m_Ember.Xforms(); }
|
||||
template <typename T, typename bucketT> Xform<T>* Renderer<T, bucketT>::NonConstXforms() { return m_Ember.NonConstXforms(); }
|
||||
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::XformCount() const { return m_Ember.XformCount(); }
|
||||
template <typename T, typename bucketT> const Xform<T>* Renderer<T, bucketT>::FinalXform() const { return m_Ember.FinalXform(); }
|
||||
template <typename T, typename bucketT> Xform<T>* Renderer<T, bucketT>::NonConstFinalXform() { return m_Ember.NonConstFinalXform(); }
|
||||
template <typename T, typename bucketT> bool Renderer<T, bucketT>::UseFinalXform() const { return m_Ember.UseFinalXform(); }
|
||||
template <typename T, typename bucketT> const Palette<float>* Renderer<T, bucketT>::GetPalette() const { return &m_Ember.m_Palette; }
|
||||
template <typename T, typename bucketT> ePaletteMode Renderer<T, bucketT>::PaletteMode() const { return m_Ember.m_PaletteMode; }
|
||||
|
||||
/// <summary>
|
||||
/// Virtual ember wrappers overridden from RendererBase, getters only.
|
||||
@ -1528,53 +1494,52 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
|
||||
{
|
||||
size_t histIndex, intColorIndex, histSize = m_HistBuckets.size();
|
||||
bucketT colorIndex, colorIndexFrac;
|
||||
auto dmap = palette->m_Entries.data();
|
||||
|
||||
//It's critical to understand what's going on here as it's one of the most important parts of the algorithm.
|
||||
//A color value gets retrieved from the palette and
|
||||
//its RGB values are added to the existing RGB values in the histogram bucket.
|
||||
//Alpha is always 1 in the palettes, so that serves as the hit count.
|
||||
//This differs from the original since redundantly adding both an alpha component and a hit count is omitted.
|
||||
//This will eventually leave us with large values for pixels with many hits, which will be log scaled down later.
|
||||
//Original used a function called bump_no_overflow(). Just do a straight add because the type will always be float or double.
|
||||
//Doing so gives a 25% speed increase.
|
||||
//Splitting these conditionals into separate loops makes no speed difference.
|
||||
for (size_t i = 0; i < sampleCount && !m_Abort; i++)
|
||||
//Linear is a linear scale for when the color index is not a whole number, which is most of the time.
|
||||
//It uses a portion of the value of the index, and the remainder of the next index.
|
||||
//Example: index = 25.7
|
||||
//Fraction = 0.7
|
||||
//Color = (dmap[25] * 0.3) + (dmap[26] * 0.7)
|
||||
//Use overloaded addition and multiplication operators in vec4 to perform the accumulation.
|
||||
if (PaletteMode() == ePaletteMode::PALETTE_LINEAR)
|
||||
{
|
||||
Point<T> p(samples[i]);//Slightly faster to cache this.
|
||||
|
||||
if (Rotate() != 0)
|
||||
//It's critical to understand what's going on here as it's one of the most important parts of the algorithm.
|
||||
//A color value gets retrieved from the palette and
|
||||
//its RGB values are added to the existing RGB values in the histogram bucket.
|
||||
//Alpha is always 1 in the palettes, so that serves as the hit count.
|
||||
//This differs from the original since redundantly adding both an alpha component and a hit count is omitted.
|
||||
//This will eventually leave us with large values for pixels with many hits, which will be log scaled down later.
|
||||
//Original used a function called bump_no_overflow(). Just do a straight add because the type will always be float or double.
|
||||
//Doing so gives a 25% speed increase.
|
||||
//Splitting these conditionals into separate loops makes no speed difference.
|
||||
for (size_t i = 0; i < sampleCount && !m_Abort; i++)
|
||||
{
|
||||
T p00 = p.m_X - CenterX();
|
||||
T p11 = p.m_Y - m_Ember.m_RotCenterY;
|
||||
p.m_X = (p00 * m_RotMat.A()) + (p11 * m_RotMat.B()) + CenterX();
|
||||
p.m_Y = (p00 * m_RotMat.D()) + (p11 * m_RotMat.E()) + m_Ember.m_RotCenterY;
|
||||
}
|
||||
Point<T> p(samples[i]);//Slightly faster to cache this.
|
||||
|
||||
//Checking this first before converting gives better performance than converting and checking a single value, which the original did.
|
||||
//Second, an interesting optimization observation is that when keeping the bounds vars within m_CarToRas and calling its InBounds() member function,
|
||||
//rather than here as members, about a 7% speedup is achieved. This is possibly due to the fact that data from m_CarToRas is accessed
|
||||
//right after the call to Convert(), so some caching efficiencies get realized.
|
||||
if (m_CarToRas.InBounds(p))
|
||||
{
|
||||
if (p.m_VizAdjusted != 0)
|
||||
if (Rotate() != 0)
|
||||
{
|
||||
m_CarToRas.Convert(p, histIndex);
|
||||
T p00 = p.m_X - CenterX();
|
||||
T p11 = p.m_Y - m_Ember.m_RotCenterY;
|
||||
p.m_X = (p00 * m_RotMat.A()) + (p11 * m_RotMat.B()) + CenterX();
|
||||
p.m_Y = (p00 * m_RotMat.D()) + (p11 * m_RotMat.E()) + m_Ember.m_RotCenterY;
|
||||
}
|
||||
|
||||
//There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test,
|
||||
//but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform one final check before proceeding.
|
||||
//This will result in a few points at the very edges getting discarded, but prevents a crash and doesn't seem to make a speed difference.
|
||||
if (histIndex < histSize)
|
||||
//Checking this first before converting gives better performance than converting and checking a single value, which the original did.
|
||||
//Second, an interesting optimization observation is that when keeping the bounds vars within m_CarToRas and calling its InBounds() member function,
|
||||
//rather than here as members, about a 7% speedup is achieved. This is possibly due to the fact that data from m_CarToRas is accessed
|
||||
//right after the call to Convert(), so some caching efficiencies get realized.
|
||||
if (m_CarToRas.InBounds(p))
|
||||
{
|
||||
if (p.m_VizAdjusted != 0)
|
||||
{
|
||||
//Linear is a linear scale for when the color index is not a whole number, which is most of the time.
|
||||
//It uses a portion of the value of the index, and the remainder of the next index.
|
||||
//Example: index = 25.7
|
||||
//Fraction = 0.7
|
||||
//Color = (dmap[25] * 0.3) + (dmap[26] * 0.7)
|
||||
//Use overloaded addition and multiplication operators in vec4 to perform the accumulation.
|
||||
if (PaletteMode() == ePaletteMode::PALETTE_LINEAR)
|
||||
m_CarToRas.Convert(p, histIndex);
|
||||
|
||||
//There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test,
|
||||
//but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform one final check before proceeding.
|
||||
//This will result in a few points at the very edges getting discarded, but prevents a crash and doesn't seem to make a speed difference.
|
||||
if (histIndex < histSize)
|
||||
{
|
||||
colorIndex = bucketT(p.m_ColorX) * COLORMAP_LENGTH;
|
||||
colorIndex = bucketT(p.m_ColorX) * COLORMAP_LENGTH_MINUS_1;
|
||||
intColorIndex = size_t(colorIndex);
|
||||
|
||||
if (intColorIndex < 0)
|
||||
@ -1614,9 +1579,33 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
|
||||
hist[3] += ((pal[3] * cifm1) + (pal2[3] * colorIndexFrac)) * va;
|
||||
}
|
||||
}
|
||||
else if (PaletteMode() == ePaletteMode::PALETTE_STEP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PaletteMode() == ePaletteMode::PALETTE_STEP)//Duplicate of above, but for step mode.
|
||||
{
|
||||
for (size_t i = 0; i < sampleCount && !m_Abort; i++)
|
||||
{
|
||||
Point<T> p(samples[i]);//Slightly faster to cache this.
|
||||
|
||||
if (Rotate() != 0)
|
||||
{
|
||||
T p00 = p.m_X - CenterX();
|
||||
T p11 = p.m_Y - m_Ember.m_RotCenterY;
|
||||
p.m_X = (p00 * m_RotMat.A()) + (p11 * m_RotMat.B()) + CenterX();
|
||||
p.m_Y = (p00 * m_RotMat.D()) + (p11 * m_RotMat.E()) + m_Ember.m_RotCenterY;
|
||||
}
|
||||
|
||||
if (m_CarToRas.InBounds(p))
|
||||
{
|
||||
if (p.m_VizAdjusted != 0)
|
||||
{
|
||||
m_CarToRas.Convert(p, histIndex);
|
||||
|
||||
if (histIndex < histSize)
|
||||
{
|
||||
intColorIndex = Clamp<size_t>(size_t(p.m_ColorX * COLORMAP_LENGTH), 0, COLORMAP_LENGTH_MINUS_1);
|
||||
intColorIndex = Clamp<size_t>(size_t(p.m_ColorX * COLORMAP_LENGTH_MINUS_1), 0, COLORMAP_LENGTH_MINUS_1);
|
||||
bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[histIndex]);//Vectorizer can't tell these point to different locations.
|
||||
const bucketT* __restrict pal = glm::value_ptr(palette->m_Entries[intColorIndex]);
|
||||
|
||||
@ -1654,7 +1643,13 @@ template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::AddToAccum(const tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj)
|
||||
{
|
||||
if (j + jj >= 0 && j + jj < intmax_t(m_SuperRasH) && i + ii >= 0 && i + ii < intmax_t(m_SuperRasW))
|
||||
m_AccumulatorBuckets[(i + ii) + ((j + jj) * m_SuperRasW)] += bucket;
|
||||
{
|
||||
auto* __restrict accum = m_AccumulatorBuckets.data() + ((i + ii) + ((j + jj) * m_SuperRasW));//For vectorizer, results in a 33% speedup.
|
||||
accum->r += bucket.r;
|
||||
accum->g += bucket.g;
|
||||
accum->b += bucket.b;
|
||||
accum->a += bucket.a;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -107,33 +107,33 @@ public:
|
||||
virtual DensityFilterBase* GetDensityFilter() override;
|
||||
|
||||
//Non-virtual ember wrappers, getters only.
|
||||
inline bool XaosPresent() const;
|
||||
inline size_t Supersample() const;
|
||||
inline size_t PaletteIndex() const;
|
||||
inline T Time() const;
|
||||
inline T Quality() const;
|
||||
inline T SpatialFilterRadius() const;
|
||||
inline T PixelsPerUnit() const;
|
||||
inline T Zoom() const;
|
||||
inline T CenterX() const;
|
||||
inline T CenterY() const;
|
||||
inline T Rotate() const;
|
||||
inline T Hue() const;
|
||||
inline bucketT Brightness() const;
|
||||
inline bucketT Contrast() const;
|
||||
inline bucketT Gamma() const;
|
||||
inline bucketT Vibrancy() const;
|
||||
inline bucketT GammaThresh() const;
|
||||
inline bucketT HighlightPower() const;
|
||||
inline Color<T> Background() const;
|
||||
inline const Xform<T>* Xforms() const;
|
||||
inline Xform<T>* NonConstXforms();
|
||||
inline size_t XformCount() const;
|
||||
inline const Xform<T>* FinalXform() const;
|
||||
inline Xform<T>* NonConstFinalXform();
|
||||
inline bool UseFinalXform() const;
|
||||
inline const Palette<T>* GetPalette() const;
|
||||
inline ePaletteMode PaletteMode() const;
|
||||
inline bool XaosPresent() const;
|
||||
inline size_t Supersample() const;
|
||||
inline size_t PaletteIndex() const;
|
||||
inline T Time() const;
|
||||
inline T Quality() const;
|
||||
inline T SpatialFilterRadius() const;
|
||||
inline T PixelsPerUnit() const;
|
||||
inline T Zoom() const;
|
||||
inline T CenterX() const;
|
||||
inline T CenterY() const;
|
||||
inline T Rotate() const;
|
||||
inline T Hue() const;
|
||||
inline bucketT Brightness() const;
|
||||
inline bucketT Contrast() const;
|
||||
inline bucketT Gamma() const;
|
||||
inline bucketT Vibrancy() const;
|
||||
inline bucketT GammaThresh() const;
|
||||
inline bucketT HighlightPower() const;
|
||||
inline Color<T> Background() const;
|
||||
inline const Xform<T>* Xforms() const;
|
||||
inline Xform<T>* NonConstXforms();
|
||||
inline size_t XformCount() const;
|
||||
inline const Xform<T>* FinalXform() const;
|
||||
inline Xform<T>* NonConstFinalXform();
|
||||
inline bool UseFinalXform() const;
|
||||
inline const Palette<float>* GetPalette() const;
|
||||
inline ePaletteMode PaletteMode() const;
|
||||
|
||||
//Virtual ember wrappers overridden from RendererBase, getters only.
|
||||
virtual size_t TemporalSamples() const override;
|
||||
|
@ -597,7 +597,6 @@ public:
|
||||
int var, samed, multid, samepost;
|
||||
glm::length_t i, j, k, n;
|
||||
size_t varCount = m_VariationList->Size();
|
||||
Palette<T> palette;
|
||||
static size_t xformDistrib[] =
|
||||
{
|
||||
2, 2, 2, 2,
|
||||
@ -863,8 +862,8 @@ public:
|
||||
for (i = 0; i < m_Renderer->FinalDimensions(); i++)
|
||||
{
|
||||
m_Hist[(p[0] * res / 256) +
|
||||
(p[1] * res / 256) * res +
|
||||
(p[2] * res / 256) * res * res]++;//A specific histogram index representing the sum of R,G,B values.
|
||||
(p[1] * res / 256) * res +
|
||||
(p[2] * res / 256) * res * res]++;//A specific histogram index representing the sum of R,G,B values.
|
||||
p += m_Renderer->PixelSize();//Advance the pointer by 1 pixel.
|
||||
}
|
||||
|
||||
@ -1366,7 +1365,7 @@ private:
|
||||
unique_ptr<XaosIterator<T>> m_XaosIterator = make_unique<XaosIterator<T>>();
|
||||
unique_ptr<Renderer<T, bucketT>> m_Renderer;
|
||||
QTIsaac<ISAAC_SIZE, ISAAC_INT> m_Rand;
|
||||
PaletteList<T> m_PaletteList;
|
||||
PaletteList<float> m_PaletteList;
|
||||
shared_ptr<VariationList<T>> m_VariationList;
|
||||
};
|
||||
}
|
||||
|
@ -53,6 +53,18 @@ static inline bool Contains(c& container, const T& val)
|
||||
return std::find_if(container.begin(), container.end(), [&](const T & t) -> bool { return t == val; }) != container.end();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin specialization for string because the compiler can't figure out the container template type
|
||||
/// when passing string to the function above.
|
||||
/// </summary>
|
||||
/// <param name="str1">The string to call find() on</param>
|
||||
/// <param name="str2">The string to search for</param>
|
||||
/// <returns>True if str2 was present at least once, else false.</returns>
|
||||
static bool Find(const string& str1, const string& str2)
|
||||
{
|
||||
return str1.find(str2) != string::npos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper around computing the total size of a vector.
|
||||
/// </summary>
|
||||
@ -284,16 +296,21 @@ static bool ReadFile(const char* filename, string& buf, bool nullTerminate = tru
|
||||
{
|
||||
try
|
||||
{
|
||||
ifstream ifs(filename, ios::binary | ios::ate);
|
||||
auto pos = ifs.tellg();
|
||||
buf.resize(pos + streampos(nullTerminate ? 1 : 0));
|
||||
ifs.seekg(0, ios::beg);
|
||||
ifs.read(&buf[0], pos);
|
||||
ifstream ifs;
|
||||
ifs.exceptions(ifstream::failbit);
|
||||
ifs.open(filename, ios::binary | ios::ate);
|
||||
|
||||
if (nullTerminate)//Optionally NULL terminate if they want to treat it as a string.
|
||||
buf[buf.size() - 1] = 0;
|
||||
if (auto pos = ifs.tellg())//Ensure it exists and wasn't empty.
|
||||
{
|
||||
buf.resize(pos + streampos(nullTerminate ? 1 : 0));
|
||||
ifs.seekg(0, ios::beg);
|
||||
ifs.read(&buf[0], pos);
|
||||
|
||||
return true;
|
||||
if (nullTerminate)//Optionally NULL terminate if they want to treat it as a string.
|
||||
buf[buf.size() - 1] = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@ -812,6 +829,18 @@ static inline T NormalizeDeg180(T angle)
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether the passed in string ends with the passed in suffix, case sensitive.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to test</param>
|
||||
/// <param name="suffix">The string to test for</param>
|
||||
/// <returns>True if str ends with suffix, else false.</returns>
|
||||
static bool EndsWith(const std::string& str, const std::string& suffix)
|
||||
{
|
||||
return str.size() >= suffix.size() &&
|
||||
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a lower case copy of a string.
|
||||
/// </summary>
|
||||
@ -867,21 +896,61 @@ static string Trim(const string& str, char ch = ' ')
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a copy of a file path string with the path portion removed.
|
||||
/// Split a string into tokens and place them in a vector.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to split</param>
|
||||
/// <param name="del">The delimiter to split the string on. Note that only one character has to match, so this is a good way to use multiple delimiters</param>
|
||||
/// <param name="removeEmpty">True to omit empty strings, else false to keep them.</param>
|
||||
/// <returns>The vector containing the split tokens</returns>
|
||||
static vector<std::string> Split(const string& str, const string& del, bool removeEmpty = false)
|
||||
{
|
||||
int current = 0;
|
||||
int next = -1;
|
||||
vector<string> vec;
|
||||
|
||||
do
|
||||
{
|
||||
current = next + 1;
|
||||
next = int(str.find_first_of(del, current));
|
||||
string ent(Trim(str.substr(current, next - current)));
|
||||
|
||||
if (!removeEmpty || ent.length() > 0)
|
||||
vec.push_back(ent);
|
||||
}
|
||||
while (next != int(string::npos));
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a copy of a file path string with the file portion removed.
|
||||
/// </summary>
|
||||
/// <param name="filename">The string to retrieve the path from</param>
|
||||
/// <returns>The path portion of the string</returns>
|
||||
static string GetPath(const string& filename)
|
||||
{
|
||||
const size_t lastSlash = filename.find_last_of("\\/");
|
||||
|
||||
if (std::string::npos != lastSlash)
|
||||
return filename.substr(0, lastSlash + 1);
|
||||
else
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a copy of a file path string with the path portion removed.
|
||||
/// </summary>
|
||||
/// <param name="filename">The string to retrieve the filename from</param>
|
||||
/// <returns>The filename portion of the string</returns>
|
||||
static string GetFilename(const string& filename)
|
||||
{
|
||||
string s;
|
||||
const size_t lastSlash = filename.find_last_of("\\/");
|
||||
|
||||
if (std::string::npos != lastSlash)
|
||||
s = filename.substr(0, lastSlash + 1);
|
||||
return filename.substr(lastSlash + 1, filename.size() - lastSlash);
|
||||
else
|
||||
s = "";
|
||||
|
||||
return s;
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -44,329 +44,334 @@ enum class eVariationAssignType : et
|
||||
/// </summary>
|
||||
enum class eVariationId : et
|
||||
{
|
||||
VAR_ARCH ,
|
||||
VAR_AUGER ,
|
||||
VAR_BARYCENTROID ,
|
||||
VAR_BCIRCLE ,
|
||||
VAR_BCOLLIDE ,
|
||||
VAR_BENT ,
|
||||
VAR_BENT2 ,
|
||||
VAR_BIPOLAR ,
|
||||
VAR_BISPLIT ,
|
||||
VAR_BLADE ,
|
||||
VAR_BLADE3D ,
|
||||
VAR_BLOB ,
|
||||
VAR_BLOB2 ,
|
||||
VAR_BLOB3D ,
|
||||
VAR_BLOCKY ,
|
||||
VAR_BLUR ,
|
||||
VAR_BLUR_CIRCLE ,
|
||||
VAR_BLUR_HEART ,
|
||||
VAR_BLUR_LINEAR ,
|
||||
VAR_BLUR_PIXELIZE ,
|
||||
VAR_BLUR_SQUARE ,
|
||||
VAR_BLUR_ZOOM ,
|
||||
VAR_BLUR3D ,
|
||||
VAR_BMOD ,
|
||||
VAR_BOARDERS ,
|
||||
VAR_BOARDERS2 ,
|
||||
VAR_BSWIRL ,
|
||||
VAR_BTRANSFORM ,
|
||||
VAR_BUBBLE ,
|
||||
VAR_BUBBLE2 ,
|
||||
VAR_BUBBLET3D ,
|
||||
VAR_BUTTERFLY ,
|
||||
VAR_BWRAPS ,
|
||||
VAR_CARDIOID ,
|
||||
VAR_CELL ,
|
||||
VAR_CHECKS ,
|
||||
VAR_CIRCLEBLUR ,
|
||||
VAR_CIRCLECROP ,
|
||||
VAR_CIRCLELINEAR ,
|
||||
VAR_CIRCLERAND ,
|
||||
VAR_CIRCLETRANS1 ,
|
||||
VAR_CIRCLIZE ,
|
||||
VAR_CIRCLIZE2 ,
|
||||
VAR_CIRCUS ,
|
||||
VAR_COLLIDEOSCOPE ,
|
||||
VAR_CONIC ,
|
||||
VAR_COS ,
|
||||
VAR_COS_WRAP ,
|
||||
VAR_COSH ,
|
||||
VAR_COSHQ ,
|
||||
VAR_COSINE ,
|
||||
VAR_COSQ ,
|
||||
VAR_COT ,
|
||||
VAR_COTH ,
|
||||
VAR_COTHQ ,
|
||||
VAR_COTQ ,
|
||||
VAR_CPOW ,
|
||||
VAR_CPOW2 ,
|
||||
VAR_CRACKLE ,
|
||||
VAR_CRESCENTS ,
|
||||
VAR_CROB ,
|
||||
VAR_CROP ,
|
||||
VAR_CROPN ,
|
||||
VAR_CROSS ,
|
||||
VAR_CSC ,
|
||||
VAR_CSCH ,
|
||||
VAR_CSCHQ ,
|
||||
VAR_CSCQ ,
|
||||
VAR_CUBIC3D ,
|
||||
VAR_ARCH,
|
||||
VAR_AUGER ,
|
||||
VAR_BARYCENTROID,
|
||||
VAR_BCIRCLE ,
|
||||
VAR_BCOLLIDE ,
|
||||
VAR_BENT ,
|
||||
VAR_BENT2 ,
|
||||
VAR_BIPOLAR ,
|
||||
VAR_BISPLIT ,
|
||||
VAR_BLADE ,
|
||||
VAR_BLADE3D ,
|
||||
VAR_BLOB ,
|
||||
VAR_BLOB2 ,
|
||||
VAR_BLOB3D ,
|
||||
VAR_BLOCKY ,
|
||||
VAR_BLUR ,
|
||||
VAR_BLUR_CIRCLE ,
|
||||
VAR_BLUR_HEART,
|
||||
VAR_BLUR_LINEAR ,
|
||||
VAR_BLUR_PIXELIZE,
|
||||
VAR_BLUR_SQUARE ,
|
||||
VAR_BLUR_ZOOM ,
|
||||
VAR_BLUR3D ,
|
||||
VAR_BMOD ,
|
||||
VAR_BOARDERS ,
|
||||
VAR_BOARDERS2 ,
|
||||
VAR_BSWIRL ,
|
||||
VAR_BTRANSFORM ,
|
||||
VAR_BUBBLE ,
|
||||
VAR_BUBBLE2 ,
|
||||
VAR_BUBBLET3D ,
|
||||
VAR_BUTTERFLY ,
|
||||
VAR_BWRAPS ,
|
||||
VAR_CARDIOID ,
|
||||
VAR_CELL ,
|
||||
VAR_CHECKS ,
|
||||
VAR_CIRCLEBLUR ,
|
||||
VAR_CIRCLECROP,
|
||||
VAR_CIRCLELINEAR,
|
||||
VAR_CIRCLERAND,
|
||||
VAR_CIRCLESPLIT,
|
||||
VAR_CIRCLETRANS1,
|
||||
VAR_CIRCLIZE ,
|
||||
VAR_CIRCLIZE2 ,
|
||||
VAR_CIRCUS,
|
||||
VAR_COLLIDEOSCOPE,
|
||||
VAR_CONIC ,
|
||||
VAR_COS ,
|
||||
VAR_COS_WRAP ,
|
||||
VAR_COSH ,
|
||||
VAR_COSHQ,
|
||||
VAR_COSINE ,
|
||||
VAR_COSQ,
|
||||
VAR_COT ,
|
||||
VAR_COTH ,
|
||||
VAR_COTHQ ,
|
||||
VAR_COTQ ,
|
||||
VAR_CPOW ,
|
||||
VAR_CPOW2 ,
|
||||
VAR_CRACKLE ,
|
||||
VAR_CRESCENTS ,
|
||||
VAR_CROB ,
|
||||
VAR_CROP ,
|
||||
VAR_CROPN ,
|
||||
VAR_CROSS ,
|
||||
VAR_CSC ,
|
||||
VAR_CSCH ,
|
||||
VAR_CSCHQ ,
|
||||
VAR_CSCQ ,
|
||||
VAR_CUBIC3D ,
|
||||
VAR_CUBIC_LATTICE3D,
|
||||
VAR_CURL ,
|
||||
VAR_CURL3D ,
|
||||
VAR_CURL_SP ,
|
||||
VAR_CURVATURE ,
|
||||
VAR_CURVE ,
|
||||
VAR_CYLINDER ,
|
||||
VAR_DELTA_A ,
|
||||
VAR_DEPTH ,
|
||||
VAR_DIAMOND ,
|
||||
VAR_DISC ,
|
||||
VAR_DISC2 ,
|
||||
VAR_DISC3D ,
|
||||
VAR_ECLIPSE ,
|
||||
VAR_ECOLLIDE ,
|
||||
VAR_EDISC ,
|
||||
VAR_EJULIA ,
|
||||
VAR_ELLIPTIC ,
|
||||
VAR_EMOD ,
|
||||
VAR_EMOTION ,
|
||||
VAR_ENNEPERS ,
|
||||
VAR_EPISPIRAL ,
|
||||
VAR_EPUSH ,
|
||||
VAR_ERF ,
|
||||
VAR_EROTATE ,
|
||||
VAR_ESCALE ,
|
||||
VAR_ESCHER ,
|
||||
VAR_ESTIQ ,
|
||||
VAR_ESWIRL ,
|
||||
VAR_EX ,
|
||||
VAR_EXP ,
|
||||
VAR_EXPO ,
|
||||
VAR_EXPONENTIAL ,
|
||||
VAR_EXTRUDE ,
|
||||
VAR_EYEFISH ,
|
||||
VAR_FALLOFF ,
|
||||
VAR_FALLOFF2 ,
|
||||
VAR_FALLOFF3 ,
|
||||
VAR_FAN ,
|
||||
VAR_FAN2 ,
|
||||
VAR_FARBLUR ,
|
||||
VAR_FDISC ,
|
||||
VAR_FIBONACCI ,
|
||||
VAR_FIBONACCI2 ,
|
||||
VAR_FISHEYE ,
|
||||
VAR_FLATTEN ,
|
||||
VAR_FLIP_CIRCLE ,
|
||||
VAR_FLIP_Y ,
|
||||
VAR_FLOWER ,
|
||||
VAR_FLUX ,
|
||||
VAR_FOCI ,
|
||||
VAR_FOCI3D ,
|
||||
VAR_FOURTH ,
|
||||
VAR_FUNNEL ,
|
||||
VAR_GAMMA ,
|
||||
VAR_GAUSSIAN_BLUR ,
|
||||
VAR_GDOFFS ,
|
||||
VAR_GLYNNIA ,
|
||||
VAR_GLYNNSIM1 ,
|
||||
VAR_GLYNNSIM2 ,
|
||||
VAR_GLYNNSIM3 ,
|
||||
VAR_GRIDOUT ,
|
||||
VAR_HANDKERCHIEF ,
|
||||
VAR_HEART ,
|
||||
VAR_HEAT ,
|
||||
VAR_HEMISPHERE ,
|
||||
VAR_HEXAPLAY3D ,
|
||||
VAR_HEXCROP ,
|
||||
VAR_HEXES ,
|
||||
VAR_HEXNIX3D ,
|
||||
VAR_HO ,
|
||||
VAR_HOLE ,
|
||||
VAR_HORSESHOE ,
|
||||
VAR_HYPERBOLIC ,
|
||||
VAR_HYPERTILE ,
|
||||
VAR_HYPERTILE1 ,
|
||||
VAR_HYPERTILE2 ,
|
||||
VAR_HYPERTILE3D ,
|
||||
VAR_HYPERTILE3D1 ,
|
||||
VAR_HYPERTILE3D2 ,
|
||||
VAR_IDISC ,
|
||||
VAR_INTERFERENCE2 ,
|
||||
VAR_JAC_CN ,
|
||||
VAR_JAC_DN ,
|
||||
VAR_JAC_SN ,
|
||||
VAR_JULIA ,
|
||||
VAR_JULIA3D ,
|
||||
VAR_JULIA3DQ ,
|
||||
VAR_JULIA3DZ ,
|
||||
VAR_JULIAC ,
|
||||
VAR_JULIAN ,
|
||||
VAR_JULIAN2 ,
|
||||
VAR_JULIAN3DX ,
|
||||
VAR_JULIANAB ,
|
||||
VAR_JULIAQ ,
|
||||
VAR_JULIASCOPE ,
|
||||
VAR_KALEIDOSCOPE ,
|
||||
VAR_LAZY_TRAVIS ,
|
||||
VAR_LAZYSUSAN ,
|
||||
VAR_LINE ,
|
||||
VAR_LINEAR ,
|
||||
VAR_LINEAR_T ,
|
||||
VAR_LINEAR_T3D ,
|
||||
VAR_CURL ,
|
||||
VAR_CURL3D ,
|
||||
VAR_CURL_SP,
|
||||
VAR_CURVATURE,
|
||||
VAR_CURVE ,
|
||||
VAR_CYLINDER ,
|
||||
VAR_CYLINDER2,
|
||||
VAR_DELTA_A ,
|
||||
VAR_DEPTH,
|
||||
VAR_DIAMOND ,
|
||||
VAR_DISC ,
|
||||
VAR_DISC2 ,
|
||||
VAR_DISC3D ,
|
||||
VAR_ECLIPSE ,
|
||||
VAR_ECOLLIDE ,
|
||||
VAR_EDISC ,
|
||||
VAR_EJULIA ,
|
||||
VAR_ELLIPTIC ,
|
||||
VAR_EMOD ,
|
||||
VAR_EMOTION ,
|
||||
VAR_ENNEPERS ,
|
||||
VAR_EPISPIRAL ,
|
||||
VAR_EPUSH ,
|
||||
VAR_ERF ,
|
||||
VAR_EROTATE ,
|
||||
VAR_ESCALE ,
|
||||
VAR_ESCHER ,
|
||||
VAR_ESTIQ,
|
||||
VAR_ESWIRL ,
|
||||
VAR_EX ,
|
||||
VAR_EXP ,
|
||||
VAR_EXPO ,
|
||||
VAR_EXPONENTIAL ,
|
||||
VAR_EXTRUDE ,
|
||||
VAR_EYEFISH ,
|
||||
VAR_FALLOFF ,
|
||||
VAR_FALLOFF2 ,
|
||||
VAR_FALLOFF3 ,
|
||||
VAR_FAN ,
|
||||
VAR_FAN2 ,
|
||||
VAR_FARBLUR,
|
||||
VAR_FDISC ,
|
||||
VAR_FIBONACCI ,
|
||||
VAR_FIBONACCI2 ,
|
||||
VAR_FISHEYE ,
|
||||
VAR_FLATTEN ,
|
||||
VAR_FLIP_CIRCLE ,
|
||||
VAR_FLIP_Y ,
|
||||
VAR_FLOWER ,
|
||||
VAR_FLUX ,
|
||||
VAR_FOCI ,
|
||||
VAR_FOCI3D ,
|
||||
VAR_FOURTH,
|
||||
VAR_FUNNEL ,
|
||||
VAR_GAMMA ,
|
||||
VAR_GAUSSIAN_BLUR,
|
||||
VAR_GDOFFS,
|
||||
VAR_GLYNNIA ,
|
||||
VAR_GLYNNSIM1 ,
|
||||
VAR_GLYNNSIM2 ,
|
||||
VAR_GLYNNSIM3 ,
|
||||
VAR_GRIDOUT ,
|
||||
VAR_HANDKERCHIEF,
|
||||
VAR_HEART ,
|
||||
VAR_HEAT,
|
||||
VAR_HEMISPHERE ,
|
||||
VAR_HEXAPLAY3D ,
|
||||
VAR_HEXCROP ,
|
||||
VAR_HEXES ,
|
||||
VAR_HEXNIX3D ,
|
||||
VAR_HO ,
|
||||
VAR_HOLE ,
|
||||
VAR_HORSESHOE ,
|
||||
VAR_HYPERBOLIC ,
|
||||
VAR_HYPERTILE ,
|
||||
VAR_HYPERTILE1 ,
|
||||
VAR_HYPERTILE2 ,
|
||||
VAR_HYPERTILE3D ,
|
||||
VAR_HYPERTILE3D1,
|
||||
VAR_HYPERTILE3D2,
|
||||
VAR_IDISC ,
|
||||
VAR_INTERFERENCE2,
|
||||
VAR_JAC_CN ,
|
||||
VAR_JAC_DN ,
|
||||
VAR_JAC_SN ,
|
||||
VAR_JULIA ,
|
||||
VAR_JULIA3D ,
|
||||
VAR_JULIA3DQ ,
|
||||
VAR_JULIA3DZ ,
|
||||
VAR_JULIAC,
|
||||
VAR_JULIAN ,
|
||||
VAR_JULIAN2 ,
|
||||
VAR_JULIAN3DX,
|
||||
VAR_JULIANAB,
|
||||
VAR_JULIAQ ,
|
||||
VAR_JULIASCOPE ,
|
||||
VAR_KALEIDOSCOPE,
|
||||
VAR_LAZY_TRAVIS ,
|
||||
VAR_LAZYSUSAN ,
|
||||
VAR_LINE ,
|
||||
VAR_LINEAR ,
|
||||
VAR_LINEAR_T ,
|
||||
VAR_LINEAR_T3D ,
|
||||
//VAR_LINEAR_XZ ,
|
||||
//VAR_LINEAR_YZ ,
|
||||
VAR_LINEAR3D ,
|
||||
VAR_LISSAJOUS ,
|
||||
VAR_LOG ,
|
||||
VAR_LOG_DB ,
|
||||
VAR_LOQ ,
|
||||
VAR_LOONIE ,
|
||||
VAR_LOONIE2 ,
|
||||
VAR_LOONIE3 ,
|
||||
VAR_LOONIE3D ,
|
||||
VAR_MASK ,
|
||||
VAR_MCARPET ,
|
||||
VAR_MIRROR_X ,
|
||||
VAR_MIRROR_Y ,
|
||||
VAR_MIRROR_Z ,
|
||||
VAR_MOBIQ ,
|
||||
VAR_MOBIUS ,
|
||||
VAR_MOBIUS_STRIP ,
|
||||
VAR_MOBIUSN ,
|
||||
VAR_MODULUS ,
|
||||
VAR_MURL ,
|
||||
VAR_MURL2 ,
|
||||
VAR_NBLUR ,
|
||||
VAR_NGON ,
|
||||
VAR_NOISE ,
|
||||
VAR_NPOLAR ,
|
||||
VAR_OCTAGON ,
|
||||
VAR_OCTAPOL ,
|
||||
VAR_ORTHO ,
|
||||
VAR_OSCILLOSCOPE ,
|
||||
VAR_OVOID ,
|
||||
VAR_OVOID3D ,
|
||||
VAR_PARABOLA ,
|
||||
VAR_PDJ ,
|
||||
VAR_PERSPECTIVE ,
|
||||
VAR_PETAL ,
|
||||
VAR_PHOENIX_JULIA ,
|
||||
VAR_PIE ,
|
||||
VAR_PIE3D ,
|
||||
VAR_POINCARE ,
|
||||
VAR_POINCARE3D ,
|
||||
VAR_POLAR ,
|
||||
VAR_POLAR2 ,
|
||||
VAR_POLYNOMIAL ,
|
||||
VAR_POPCORN ,
|
||||
VAR_POPCORN2 ,
|
||||
VAR_POPCORN23D ,
|
||||
VAR_POW_BLOCK ,
|
||||
VAR_POWER ,
|
||||
VAR_PRESSURE_WAVE ,
|
||||
VAR_PROSE3D ,
|
||||
VAR_PSPHERE ,
|
||||
VAR_Q_ODE ,
|
||||
VAR_RADIAL_BLUR ,
|
||||
VAR_RATIONAL3 ,
|
||||
VAR_RAYS ,
|
||||
VAR_RBLUR ,
|
||||
VAR_RECTANGLES ,
|
||||
VAR_RINGS ,
|
||||
VAR_RINGS2 ,
|
||||
VAR_RIPPLE ,
|
||||
VAR_RIPPLED ,
|
||||
VAR_ROTATE_X ,
|
||||
VAR_ROTATE_Y ,
|
||||
VAR_ROTATE_Z ,
|
||||
VAR_ROUNDSPHER ,
|
||||
VAR_ROUNDSPHER3D ,
|
||||
VAR_SCRY ,
|
||||
VAR_SCRY3D ,
|
||||
VAR_SEC ,
|
||||
VAR_SECANT2 ,
|
||||
VAR_SECH ,
|
||||
VAR_SECHQ ,
|
||||
VAR_SECQ ,
|
||||
VAR_SEPARATION ,
|
||||
VAR_SHRED_RAD ,
|
||||
VAR_SHRED_LIN ,
|
||||
VAR_SIGMOID ,
|
||||
VAR_SIN ,
|
||||
VAR_SINEBLUR ,
|
||||
VAR_SINH ,
|
||||
VAR_SINHQ ,
|
||||
VAR_SINQ ,
|
||||
VAR_SINTRANGE ,
|
||||
VAR_SINUS_GRID ,
|
||||
VAR_SINUSOIDAL ,
|
||||
VAR_SINUSOIDAL3D ,
|
||||
VAR_LINEAR3D ,
|
||||
VAR_LISSAJOUS ,
|
||||
VAR_LOG ,
|
||||
VAR_LOG_DB ,
|
||||
VAR_LOQ ,
|
||||
VAR_LOONIE ,
|
||||
VAR_LOONIE2 ,
|
||||
VAR_LOONIE3 ,
|
||||
VAR_LOONIE3D ,
|
||||
VAR_MASK ,
|
||||
VAR_MCARPET ,
|
||||
VAR_MIRROR_X,
|
||||
VAR_MIRROR_Y,
|
||||
VAR_MIRROR_Z,
|
||||
VAR_MOBIQ,
|
||||
VAR_MOBIUS ,
|
||||
VAR_MOBIUS_STRIP,
|
||||
VAR_MOBIUSN ,
|
||||
VAR_MODULUS ,
|
||||
VAR_MURL ,
|
||||
VAR_MURL2 ,
|
||||
VAR_NBLUR ,
|
||||
VAR_NGON ,
|
||||
VAR_NOISE ,
|
||||
VAR_NPOLAR ,
|
||||
VAR_OCTAGON ,
|
||||
VAR_OCTAPOL ,
|
||||
VAR_ORTHO ,
|
||||
VAR_OSCILLOSCOPE,
|
||||
VAR_OVOID ,
|
||||
VAR_OVOID3D ,
|
||||
VAR_PARABOLA ,
|
||||
VAR_PDJ ,
|
||||
VAR_PERSPECTIVE ,
|
||||
VAR_PETAL ,
|
||||
VAR_PHOENIX_JULIA,
|
||||
VAR_PIE ,
|
||||
VAR_PIE3D ,
|
||||
VAR_POINCARE ,
|
||||
VAR_POINCARE3D ,
|
||||
VAR_POLAR ,
|
||||
VAR_POLAR2 ,
|
||||
VAR_POLYNOMIAL ,
|
||||
VAR_POPCORN ,
|
||||
VAR_POPCORN2 ,
|
||||
VAR_POPCORN23D ,
|
||||
VAR_POW_BLOCK ,
|
||||
VAR_POWER ,
|
||||
VAR_PRESSURE_WAVE,
|
||||
VAR_PROSE3D ,
|
||||
VAR_PSPHERE ,
|
||||
VAR_Q_ODE,
|
||||
VAR_RADIAL_BLUR ,
|
||||
VAR_RATIONAL3 ,
|
||||
VAR_RAYS ,
|
||||
VAR_RBLUR,
|
||||
VAR_RECTANGLES ,
|
||||
VAR_RINGS ,
|
||||
VAR_RINGS2 ,
|
||||
VAR_RIPPLE ,
|
||||
VAR_RIPPLED ,
|
||||
VAR_ROTATE_X,
|
||||
VAR_ROTATE_Y,
|
||||
VAR_ROTATE_Z,
|
||||
VAR_ROUNDSPHER ,
|
||||
VAR_ROUNDSPHER3D,
|
||||
VAR_SCRY ,
|
||||
VAR_SCRY3D ,
|
||||
VAR_SEC ,
|
||||
VAR_SECANT2 ,
|
||||
VAR_SECH ,
|
||||
VAR_SECHQ,
|
||||
VAR_SECQ,
|
||||
VAR_SEPARATION ,
|
||||
VAR_SHRED_RAD ,
|
||||
VAR_SHRED_LIN ,
|
||||
VAR_SIGMOID ,
|
||||
VAR_SIN ,
|
||||
VAR_SINEBLUR ,
|
||||
VAR_SINH ,
|
||||
VAR_SINHQ ,
|
||||
VAR_SINQ ,
|
||||
VAR_SINTRANGE,
|
||||
VAR_SINUS_GRID ,
|
||||
VAR_SINUSOIDAL ,
|
||||
VAR_SINUSOIDAL3D,
|
||||
//VAR_SMARTCROP ,
|
||||
VAR_SPHERICAL ,
|
||||
VAR_SPHERICAL3D ,
|
||||
VAR_SPHERICALN ,
|
||||
VAR_SPHERIVOID ,
|
||||
VAR_SPHYP3D ,
|
||||
VAR_SPIRAL ,
|
||||
VAR_SPIRAL_WING ,
|
||||
VAR_SPIROGRAPH ,
|
||||
VAR_SPLIT ,
|
||||
VAR_SPLIT_BRDR ,
|
||||
VAR_SPLITS ,
|
||||
VAR_SPLITS3D ,
|
||||
VAR_SQUARE ,
|
||||
VAR_SQUARE3D ,
|
||||
VAR_SQUARIZE ,
|
||||
VAR_SQUIRREL ,
|
||||
VAR_SQUISH ,
|
||||
VAR_SSCHECKS ,
|
||||
VAR_STARBLUR ,
|
||||
VAR_STRIPES ,
|
||||
VAR_STWIN ,
|
||||
VAR_SUPER_SHAPE ,
|
||||
VAR_SUPER_SHAPE3D ,
|
||||
VAR_SVF ,
|
||||
VAR_SWIRL ,
|
||||
VAR_SYNTH ,
|
||||
VAR_TAN ,
|
||||
VAR_TANCOS ,
|
||||
VAR_TANGENT ,
|
||||
VAR_TANH ,
|
||||
VAR_TANHQ ,
|
||||
VAR_TANQ ,
|
||||
VAR_TARGET ,
|
||||
VAR_TAURUS ,
|
||||
VAR_TRADE ,
|
||||
VAR_TRUCHET ,
|
||||
VAR_TWINTRIAN ,
|
||||
VAR_TWO_FACE ,
|
||||
VAR_UNPOLAR ,
|
||||
VAR_VORON ,
|
||||
VAR_W ,
|
||||
VAR_WAFFLE ,
|
||||
VAR_WAVES ,
|
||||
VAR_WAVES2 ,
|
||||
VAR_WAVES23D ,
|
||||
VAR_WAVES2B ,
|
||||
VAR_WAVESN ,
|
||||
VAR_WDISC ,
|
||||
VAR_WEDGE ,
|
||||
VAR_WEDGE_JULIA ,
|
||||
VAR_WEDGE_SPH ,
|
||||
VAR_WHORL ,
|
||||
VAR_X ,
|
||||
VAR_XERF ,
|
||||
VAR_XHEART ,
|
||||
VAR_XTRB ,
|
||||
VAR_Y ,
|
||||
VAR_Z ,
|
||||
VAR_ZBLUR ,
|
||||
VAR_ZCONE ,
|
||||
VAR_ZSCALE ,
|
||||
VAR_ZTRANSLATE ,
|
||||
VAR_SPHERICAL ,
|
||||
VAR_SPHERICAL3D ,
|
||||
VAR_SPHERICALN ,
|
||||
VAR_SPHERIVOID,
|
||||
VAR_SPHYP3D ,
|
||||
VAR_SPIRAL ,
|
||||
VAR_SPIRAL_WING ,
|
||||
VAR_SPIROGRAPH ,
|
||||
VAR_SPLIT ,
|
||||
VAR_SPLIT_BRDR,
|
||||
VAR_SPLITS ,
|
||||
VAR_SPLITS3D ,
|
||||
VAR_SQUARE ,
|
||||
VAR_SQUARE3D ,
|
||||
VAR_SQUARIZE ,
|
||||
VAR_SQUIRREL ,
|
||||
VAR_SQUISH,
|
||||
VAR_SSCHECKS ,
|
||||
VAR_STARBLUR ,
|
||||
VAR_STRIPES ,
|
||||
VAR_STWIN ,
|
||||
VAR_SUPER_SHAPE ,
|
||||
VAR_SUPER_SHAPE3D,
|
||||
VAR_SVF ,
|
||||
VAR_SWIRL ,
|
||||
VAR_SYNTH ,
|
||||
VAR_TAN ,
|
||||
VAR_TANCOS,
|
||||
VAR_TANGENT ,
|
||||
VAR_TANH ,
|
||||
VAR_TANHQ ,
|
||||
VAR_TANQ ,
|
||||
VAR_TARGET ,
|
||||
VAR_TAURUS ,
|
||||
VAR_TILE_LOG,
|
||||
VAR_TRADE ,
|
||||
VAR_TRUCHET,
|
||||
VAR_TRUCHET_FILL,
|
||||
VAR_TWINTRIAN ,
|
||||
VAR_TWO_FACE ,
|
||||
VAR_UNPOLAR ,
|
||||
VAR_VORON,
|
||||
VAR_W ,
|
||||
VAR_WAFFLE,
|
||||
VAR_WAVES ,
|
||||
VAR_WAVES2 ,
|
||||
VAR_WAVES2_RADIAL,
|
||||
VAR_WAVES23D ,
|
||||
VAR_WAVES2B ,
|
||||
VAR_WAVESN ,
|
||||
VAR_WDISC ,
|
||||
VAR_WEDGE ,
|
||||
VAR_WEDGE_JULIA ,
|
||||
VAR_WEDGE_SPH ,
|
||||
VAR_WHORL ,
|
||||
VAR_X ,
|
||||
VAR_XERF ,
|
||||
VAR_XHEART ,
|
||||
VAR_XTRB ,
|
||||
VAR_Y ,
|
||||
VAR_Z ,
|
||||
VAR_ZBLUR ,
|
||||
VAR_ZCONE ,
|
||||
VAR_ZSCALE ,
|
||||
VAR_ZTRANSLATE,
|
||||
|
||||
VAR_PRE_ARCH,
|
||||
VAR_PRE_AUGER,
|
||||
@ -408,6 +413,7 @@ enum class eVariationId : et
|
||||
VAR_PRE_CIRCLECROP,
|
||||
VAR_PRE_CIRCLELINEAR,
|
||||
VAR_PRE_CIRCLERAND,
|
||||
VAR_PRE_CIRCLESPLIT,
|
||||
VAR_PRE_CIRCLETRANS1,
|
||||
VAR_PRE_CIRCLIZE,
|
||||
VAR_PRE_CIRCLIZE2,
|
||||
@ -444,6 +450,7 @@ enum class eVariationId : et
|
||||
VAR_PRE_CURVATURE,
|
||||
VAR_PRE_CURVE,
|
||||
VAR_PRE_CYLINDER,
|
||||
VAR_PRE_CYLINDER2,
|
||||
VAR_PRE_DELTA_A,
|
||||
VAR_PRE_DEPTH,
|
||||
VAR_PRE_DIAMOND,
|
||||
@ -663,8 +670,10 @@ enum class eVariationId : et
|
||||
VAR_PRE_TANQ,
|
||||
VAR_PRE_TARGET,
|
||||
VAR_PRE_TAURUS,
|
||||
VAR_PRE_TILE_LOG,
|
||||
VAR_PRE_TRADE,
|
||||
VAR_PRE_TRUCHET,
|
||||
VAR_PRE_TRUCHET_FILL,
|
||||
VAR_PRE_TWINTRIAN,
|
||||
VAR_PRE_TWO_FACE,
|
||||
VAR_PRE_UNPOLAR,
|
||||
@ -673,6 +682,7 @@ enum class eVariationId : et
|
||||
VAR_PRE_WAFFLE,
|
||||
VAR_PRE_WAVES,
|
||||
VAR_PRE_WAVES2,
|
||||
VAR_PRE_WAVES2_RADIAL,
|
||||
VAR_PRE_WAVES23D,
|
||||
VAR_PRE_WAVES2B,
|
||||
VAR_PRE_WAVESN,
|
||||
@ -732,6 +742,7 @@ enum class eVariationId : et
|
||||
VAR_POST_CIRCLECROP,
|
||||
VAR_POST_CIRCLELINEAR,
|
||||
VAR_POST_CIRCLERAND,
|
||||
VAR_POST_CIRCLESPLIT,
|
||||
VAR_POST_CIRCLETRANS1,
|
||||
VAR_POST_CIRCLIZE,
|
||||
VAR_POST_CIRCLIZE2,
|
||||
@ -768,6 +779,7 @@ enum class eVariationId : et
|
||||
VAR_POST_CURVATURE,
|
||||
VAR_POST_CURVE,
|
||||
VAR_POST_CYLINDER,
|
||||
VAR_POST_CYLINDER2,
|
||||
VAR_POST_DELTA_A,
|
||||
VAR_POST_DEPTH,
|
||||
VAR_POST_DIAMOND,
|
||||
@ -987,8 +999,10 @@ enum class eVariationId : et
|
||||
VAR_POST_TANQ,
|
||||
VAR_POST_TARGET,
|
||||
VAR_POST_TAURUS,
|
||||
VAR_POST_TILE_LOG,
|
||||
VAR_POST_TRADE,
|
||||
VAR_POST_TRUCHET,
|
||||
VAR_POST_TRUCHET_FILL,
|
||||
VAR_POST_TWINTRIAN,
|
||||
VAR_POST_TWO_FACE,
|
||||
VAR_POST_UNPOLAR,
|
||||
@ -997,6 +1011,7 @@ enum class eVariationId : et
|
||||
VAR_POST_WAFFLE,
|
||||
VAR_POST_WAVES,
|
||||
VAR_POST_WAVES2,
|
||||
VAR_POST_WAVES2_RADIAL,
|
||||
VAR_POST_WAVES23D,
|
||||
VAR_POST_WAVES2B,
|
||||
VAR_POST_WAVESN,
|
||||
|
@ -344,6 +344,11 @@ VariationList<T>::VariationList()
|
||||
ADDPREPOSTREGVAR(Gamma)
|
||||
ADDPREPOSTREGVAR(PRose3D)
|
||||
ADDPREPOSTREGVAR(LogDB)
|
||||
ADDPREPOSTREGVAR(CircleSplit)
|
||||
ADDPREPOSTREGVAR(Cylinder2)
|
||||
ADDPREPOSTREGVAR(TileLog)
|
||||
ADDPREPOSTREGVAR(TruchetFill)
|
||||
ADDPREPOSTREGVAR(Waves2Radial)
|
||||
//ADDPREPOSTREGVAR(LinearXZ)
|
||||
//ADDPREPOSTREGVAR(LinearYZ)
|
||||
//DC are special.
|
||||
|
@ -3940,7 +3940,7 @@ public:
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
{
|
||||
T expx = std::exp(helper.In.x) * T(0.5);
|
||||
T expnx = T(0.25) / expx;
|
||||
T expnx = T(0.25) / Zeps(expx);
|
||||
T sn, cn, tmp;
|
||||
sincos(helper.In.y, &sn, &cn);
|
||||
tmp = m_Weight / Zeps(expx + expnx - cn);
|
||||
@ -3955,7 +3955,7 @@ public:
|
||||
intmax_t varIndex = IndexInXform();
|
||||
ss << "\t{\n"
|
||||
<< "\t\treal_t expx = exp(vIn.x) * (real_t)(0.5);\n"
|
||||
<< "\t\treal_t expnx = (real_t)(0.25) / expx;\n"
|
||||
<< "\t\treal_t expnx = (real_t)(0.25) / Zeps(expx);\n"
|
||||
<< "\t\treal_t sn = sin(vIn.y);\n"
|
||||
<< "\t\treal_t cn = cos(vIn.y);\n"
|
||||
<< "\t\treal_t tmp = Zeps(expx + expnx - cn);\n"
|
||||
|
@ -1951,7 +1951,6 @@ class DeltaAVariation : public Variation<T>
|
||||
{
|
||||
public:
|
||||
DeltaAVariation(T weight = 1.0) : Variation<T>("deltaa", eVariationId::VAR_DELTA_A, weight) { }
|
||||
|
||||
VARCOPY(DeltaAVariation)
|
||||
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
@ -4551,7 +4550,7 @@ protected:
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_S, prefix + "ripple_s"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Is, prefix + "ripple_is"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Vxp, prefix + "ripple_vxp"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Pxa , prefix + "ripple_pxa"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Pxa, prefix + "ripple_pxa"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Pixa, prefix + "ripple_pixa"));
|
||||
}
|
||||
|
||||
@ -5420,14 +5419,14 @@ public:
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
{
|
||||
T a = helper.m_PrecalcAtanyx;
|
||||
int n = rand.Rand(uint(m_Spread));
|
||||
int n = rand.Rand(m_SpreadUint);
|
||||
|
||||
if (a < 0)
|
||||
n++;
|
||||
|
||||
a += M_2PI * n;
|
||||
|
||||
if (std::cos(a * m_InvSpread) < rand.Rand() * 2 / 0xFFFFFFFF - 1)//Rand max.
|
||||
if (std::cos(a * m_InvSpread) < rand.Rand() * T(2) / 0xFFFFFFFF - T(1))//Rand max.
|
||||
a -= m_FullSpread;
|
||||
|
||||
T lnr2 = std::log(helper.m_PrecalcSumSquares);
|
||||
@ -5464,7 +5463,7 @@ public:
|
||||
<< "\n"
|
||||
<< "\t\ta += M_2PI * n;\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (cos(a * " << invSpread << ") < MwcNext(mwc) * 2 / 0xFFFFFFFF - 1)\n"
|
||||
<< "\t\tif (cos(a * " << invSpread << ") < MwcNext(mwc) * (real_t)2.0 / 0xFFFFFFFF - (real_t)1.0)\n"
|
||||
<< "\t\t a -= " << fullSpread << ";\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t lnr2 = log(precalcSumSquares);\n"
|
||||
@ -5487,6 +5486,7 @@ public:
|
||||
m_HalfD = m_D / 2;
|
||||
m_InvSpread = T(0.5) / m_Spread;
|
||||
m_FullSpread = M_2PI * m_Spread;
|
||||
m_SpreadUint = uint(m_Spread);
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -5512,7 +5512,8 @@ private:
|
||||
T m_A;
|
||||
T m_Divisor;
|
||||
T m_Spread;
|
||||
T m_C;//Precalc.
|
||||
uint m_SpreadUint;//Precalc.
|
||||
T m_C;
|
||||
T m_HalfC;
|
||||
T m_D;
|
||||
T m_HalfD;
|
||||
|
@ -1038,7 +1038,7 @@ public:
|
||||
T expx = std::exp(helper.In.x) * T(0.5);
|
||||
T expnx = T(0.25) / expx;
|
||||
T boot = helper.In.z == 0 ? helper.m_PrecalcAtanyx : helper.In.z;
|
||||
T tmp = m_Weight / (expx + expnx - (std::cos(helper.In.y) * std::cos(boot)));
|
||||
T tmp = m_Weight / Zeps(expx + expnx - (std::cos(helper.In.y) * std::cos(boot)));
|
||||
helper.Out.x = (expx - expnx) * tmp;
|
||||
helper.Out.y = std::sin(helper.In.y) * tmp;
|
||||
helper.Out.z = std::sin(boot) * tmp;
|
||||
@ -1052,7 +1052,7 @@ public:
|
||||
<< "\t\treal_t expx = exp(vIn.x) * (real_t)(0.5);\n"
|
||||
<< "\t\treal_t expnx = (real_t)(0.25) / expx;\n"
|
||||
<< "\t\treal_t boot = vIn.z == 0 ? precalcAtanyx : vIn.z;\n"
|
||||
<< "\t\treal_t tmp = xform->m_VariationWeights[" << varIndex << "] / (expx + expnx - (cos(vIn.y) * cos(boot)));\n"
|
||||
<< "\t\treal_t tmp = xform->m_VariationWeights[" << varIndex << "] / Zeps(expx + expnx - (cos(vIn.y) * cos(boot)));\n"
|
||||
<< "\n"
|
||||
<< "\t\tvOut.x = (expx - expnx) * tmp;\n"
|
||||
<< "\t\tvOut.y = sin(vIn.y) * tmp;\n"
|
||||
@ -1060,6 +1060,11 @@ public:
|
||||
<< "\t}\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
virtual vector<string> OpenCLGlobalFuncNames() const override
|
||||
{
|
||||
return vector<string> { "Zeps" };
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -4379,17 +4379,17 @@ protected:
|
||||
{
|
||||
string prefix = Prefix();
|
||||
m_Params.clear();
|
||||
m_Params.push_back(ParamWithName<T>(&m_Power , prefix + "smartcrop_power", 4)); //Original used a prefix of scrop_, which is incompatible with Ember's design.
|
||||
m_Params.push_back(ParamWithName<T>(&m_Radius , prefix + "smartcrop_radius", 1));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Roundstr , prefix + "smartcrop_roundstr"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Power, prefix + "smartcrop_power", 4)); //Original used a prefix of scrop_, which is incompatible with Ember's design.
|
||||
m_Params.push_back(ParamWithName<T>(&m_Radius, prefix + "smartcrop_radius", 1));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Roundstr, prefix + "smartcrop_roundstr"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Roundwidth, prefix + "smartcrop_roundwidth", 1));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Distortion, prefix + "smartcrop_distortion", 1));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Edge , prefix + "smartcrop_edge"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Scatter , prefix + "smartcrop_scatter"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Offset , prefix + "smartcrop_offset"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Rotation , prefix + "smartcrop_rotation"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Cropmode , prefix + "smartcrop_cropmode", 1, eParamType::INTEGER, -1, 2));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Static , prefix + "smartcrop_static", 1, eParamType::INTEGER, -1, 3));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Edge, prefix + "smartcrop_edge"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Scatter, prefix + "smartcrop_scatter"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Offset, prefix + "smartcrop_offset"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Rotation, prefix + "smartcrop_rotation"));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Cropmode, prefix + "smartcrop_cropmode", 1, eParamType::INTEGER, -1, 2));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Static , prefix + "smartcrop_static", 1, eParamType::INTEGER, -1, 3));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Mode, prefix + "smartcrop_mode"));//Precalc.
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Radial, prefix + "smartcrop_radial"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_WorkRadius, prefix + "smartcrop_work_radius"));
|
||||
@ -4649,7 +4649,7 @@ public:
|
||||
<< "\n"
|
||||
<< "\t\tif (" << hypergon << " != 0)\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t temp1 = fmod(fabs(a), M_2PI / " << hypergonN << ") - M_PI / " << hypergonN << ";\n"
|
||||
<< "\t\t temp1 = fmod(fabs(a), (real_t)M_2PI / " << hypergonN << ") - M_PI / " << hypergonN << ";\n"
|
||||
<< "\t\t temp2 = Sqr(tan(temp1)) + 1;\n"
|
||||
<< "\n"
|
||||
<< "\t\t if (temp2 >= Sqr(" << hypergonD << "))\n"
|
||||
@ -4664,7 +4664,7 @@ public:
|
||||
<< "\n"
|
||||
<< "\t\tif (" << star << "!= 0)\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t temp1 = tan(fabs(fmod(fabs(a), M_2PI / " << starN << ") - M_PI / " << starN << "));\n"
|
||||
<< "\t\t temp1 = tan(fabs(fmod(fabs(a), (real_t)M_2PI / " << starN << ") - M_PI / " << starN << "));\n"
|
||||
<< "\t\t total += " << star << " * sqrt(Sqr(" << tanStarSlope << ") * (1 + Sqr(temp1)) / Sqr(temp1 + " << tanStarSlope << "));\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\n"
|
||||
@ -4684,7 +4684,7 @@ public:
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t if (" << hypergon << " != 0.0)\n"
|
||||
<< "\t\t {\n"
|
||||
<< "\t\t temp1 = fmod(fabs(a2), M_2PI / " << hypergonN << ") - M_PI / " << hypergonN << ";\n"
|
||||
<< "\t\t temp1 = fmod(fabs(a2), (real_t)M_2PI / " << hypergonN << ") - M_PI / " << hypergonN << ";\n"
|
||||
<< "\t\t temp2 = Sqr(tan(temp1)) + 1;\n"
|
||||
<< "\n"
|
||||
<< "\t\t if (temp2 >= Sqr(" << hypergonD << "))\n"
|
||||
@ -4699,7 +4699,7 @@ public:
|
||||
<< "\n"
|
||||
<< "\t\t if (" << star << " != 0)\n"
|
||||
<< "\t\t {\n"
|
||||
<< "\t\t temp1 = tan(fabs(fmod(fabs(a2), M_2PI / " << starN << ") - M_PI / " << starN << "));\n"
|
||||
<< "\t\t temp1 = tan(fabs(fmod(fabs(a2), (real_t)M_2PI / " << starN << ") - M_PI / " << starN << "));\n"
|
||||
<< "\t\t total2 += " << star << " * sqrt(Sqr(" << tanStarSlope << ") * (1 + Sqr(temp1)) / Sqr(temp1 + " << tanStarSlope << "));\n"
|
||||
<< "\t\t }\n"
|
||||
<< "\n"
|
||||
|
@ -948,6 +948,513 @@ private:
|
||||
T m_FixPe;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// circlesplit.
|
||||
/// By tatasz.
|
||||
/// http://fav.me/dapecsh
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class CircleSplitVariation : public ParametricVariation<T>
|
||||
{
|
||||
public:
|
||||
CircleSplitVariation(T weight = 1.0) : ParametricVariation<T>("circlesplit", eVariationId::VAR_CIRCLESPLIT, weight, true, true)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
PARVARCOPY(CircleSplitVariation)
|
||||
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
{
|
||||
T x1, y1;
|
||||
|
||||
if (helper.m_PrecalcSqrtSumSquares < (m_Radius - m_Split))
|
||||
{
|
||||
x1 = helper.In.x;
|
||||
y1 = helper.In.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
T a = std::atan2(helper.In.y, helper.In.x);
|
||||
T len = helper.m_PrecalcSqrtSumSquares + m_Split;
|
||||
x1 = std::cos(a) * len;
|
||||
y1 = std::sin(a) * len;
|
||||
}
|
||||
|
||||
helper.Out.x = m_Weight * x1;
|
||||
helper.Out.y = m_Weight * y1;
|
||||
}
|
||||
|
||||
virtual string OpenCLString() const override
|
||||
{
|
||||
ostringstream ss, ss2;
|
||||
intmax_t i = 0, varIndex = IndexInXform();
|
||||
ss2 << "_" << XformIndexInEmber() << "]";
|
||||
string index = ss2.str();
|
||||
string cs_radius = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string cs_split = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
ss << "\t{\n"
|
||||
<< "\t\treal_t x1, y1;\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (precalcSqrtSumSquares < (" << cs_radius << " - " << cs_split << "))\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tx1 = vIn.x;\n"
|
||||
<< "\t\t\ty1 = vIn.y;\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\t\telse\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\treal_t a = (real_t)atan2(vIn.y, vIn.x);\n"
|
||||
<< "\t\t\treal_t len = precalcSqrtSumSquares + " << cs_split << ";\n"
|
||||
<< "\t\t\tx1 = cos(a) * len;\n"
|
||||
<< "\t\t\ty1 = sin(a) * len;\n"
|
||||
<< "\t\t}"
|
||||
<< "\t\tvOut.x = xform->m_VariationWeights[" << varIndex << "] * x1;\n"
|
||||
<< "\t\tvOut.y = xform->m_VariationWeights[" << varIndex << "] * y1;\n"
|
||||
<< "\t}\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
void Init()
|
||||
{
|
||||
string prefix = Prefix();
|
||||
m_Params.clear();
|
||||
m_Params.push_back(ParamWithName<T>(&m_Radius, prefix + "circlesplit_radius", 1));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Split, prefix + "circlesplit_split", T(0.5)));
|
||||
}
|
||||
|
||||
private:
|
||||
T m_Radius;
|
||||
T m_Split;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// cylinder2.
|
||||
/// By tatasz.
|
||||
/// http://fav.me/dapecsh
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class Cylinder2Variation : public Variation<T>
|
||||
{
|
||||
public:
|
||||
Cylinder2Variation(T weight = 1.0) : Variation<T>("cylinder2", eVariationId::VAR_CYLINDER2, weight) { }
|
||||
|
||||
VARCOPY(Cylinder2Variation)
|
||||
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
{
|
||||
helper.Out.x = m_Weight * (helper.In.x / Zeps(std::sqrt(SQR(helper.In.x) + 1)));
|
||||
helper.Out.y = m_Weight * helper.In.y;
|
||||
}
|
||||
|
||||
virtual string OpenCLString() const override
|
||||
{
|
||||
ostringstream ss;
|
||||
intmax_t varIndex = IndexInXform();
|
||||
ss << "\t{\n"
|
||||
<< "\n"
|
||||
<< "\t\tvOut.x = xform->m_VariationWeights[" << varIndex << "] * (vIn.x / Zeps(sqrt(SQR(vIn.x) + (real_t)1.0)));\n"
|
||||
<< "\t\tvOut.y = xform->m_VariationWeights[" << varIndex << "] * vIn.y;\n"
|
||||
<< "\t}\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
virtual vector<string> OpenCLGlobalFuncNames() const override
|
||||
{
|
||||
return vector<string> { "Zeps" };
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// tile_log.
|
||||
/// By zy0rg.
|
||||
/// http://zy0rg.deviantart.com
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class TileLogVariation : public ParametricVariation<T>
|
||||
{
|
||||
public:
|
||||
TileLogVariation(T weight = 1.0) : ParametricVariation<T>("tile_log", eVariationId::VAR_TILE_LOG, weight)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
PARVARCOPY(TileLogVariation)
|
||||
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
{
|
||||
T temp = Round(std::log(rand.Frand01<T>()) * (rand.Rand() & 1 ? m_Spread : -m_Spread));
|
||||
helper.Out.x = m_Weight * (helper.In.x + temp);
|
||||
helper.Out.y = m_Weight * helper.In.y;
|
||||
}
|
||||
|
||||
virtual string OpenCLString() const override
|
||||
{
|
||||
ostringstream ss, ss2;
|
||||
intmax_t i = 0, varIndex = IndexInXform();
|
||||
ss2 << "_" << XformIndexInEmber() << "]";
|
||||
string index = ss2.str();
|
||||
string spread = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
ss << "\t{\n"
|
||||
<< "\t\treal_t temp = (real_t) (Round(log(MwcNext01(mwc)) * (MwcNext(mwc) & 1 ? " << spread << " : -" << spread << ")));\n"
|
||||
<< "\n"
|
||||
<< "\t\tvOut.x = xform->m_VariationWeights[" << varIndex << "] * (vIn.x + temp);\n"
|
||||
<< "\t\tvOut.y = xform->m_VariationWeights[" << varIndex << "] * vIn.y;\n"
|
||||
<< "\t}\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
virtual vector<string> OpenCLGlobalFuncNames() const override
|
||||
{
|
||||
return vector<string> { "Round" };
|
||||
}
|
||||
|
||||
protected:
|
||||
void Init()
|
||||
{
|
||||
string prefix = Prefix();
|
||||
m_Params.clear();
|
||||
m_Params.push_back(ParamWithName<T>(&m_Spread, prefix + "tile_log_spread", 1));
|
||||
}
|
||||
|
||||
private:
|
||||
T m_Spread;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Truchet_fill.
|
||||
/// By tatasz.
|
||||
/// http://fav.me/dapecsh
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class TruchetFillVariation : public ParametricVariation<T>
|
||||
{
|
||||
public:
|
||||
TruchetFillVariation(T weight = 1.0) : ParametricVariation<T>("Truchet_fill", eVariationId::VAR_TRUCHET_FILL, weight)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
PARVARCOPY(TruchetFillVariation)
|
||||
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
{
|
||||
T modbase = T(65535);
|
||||
T multiplier = T(32747); //1103515245;
|
||||
T offset = T(12345);
|
||||
T x = helper.In.x * m_Scale;
|
||||
T y = helper.In.y * m_Scale;
|
||||
T intx = Round(x);
|
||||
T inty = Round(y);
|
||||
T r = x - intx;
|
||||
|
||||
if (r < 0)
|
||||
x = 1 + r;
|
||||
else
|
||||
x = r;
|
||||
|
||||
r = y - inty;
|
||||
|
||||
if (r < 0)
|
||||
y = 1 + r;
|
||||
else
|
||||
y = r;
|
||||
|
||||
T tiletype = 0;
|
||||
|
||||
if (m_Seed != 0)
|
||||
{
|
||||
if (m_Seed == 1)
|
||||
{
|
||||
tiletype = m_Seed;
|
||||
}
|
||||
else
|
||||
{
|
||||
T xrand = helper.In.x;
|
||||
T yrand = helper.In.y;
|
||||
xrand = Round(std::abs(xrand)) * m_Seed2;
|
||||
yrand = Round(std::abs(yrand)) * m_Seed2;
|
||||
T niter = xrand + yrand + (xrand * yrand);
|
||||
T randint = (m_Seed + niter) * m_Seed2 * T(0.5);
|
||||
randint = std::fmod((randint * multiplier + offset), modbase);
|
||||
tiletype = std::fmod(randint, T(2.0));
|
||||
}
|
||||
}
|
||||
|
||||
T r0, r1;
|
||||
|
||||
if (tiletype < T(1))
|
||||
{
|
||||
//Slow drawmode
|
||||
r0 = std::pow((std::pow(std::fabs(x), m_FinalExponent) + std::pow(std::fabs(y), m_FinalExponent)), m_OneOverEx);
|
||||
r1 = std::pow((std::pow(std::fabs(x - T(1.0)), m_FinalExponent) + std::pow(std::fabs(y - 1), m_FinalExponent)), m_OneOverEx);
|
||||
}
|
||||
else
|
||||
{
|
||||
r0 = std::pow((std::pow(std::fabs(x - T(1.0)), m_FinalExponent) + std::pow(std::fabs(y), m_FinalExponent)), m_OneOverEx);
|
||||
r1 = std::pow((std::pow(std::fabs(x), m_FinalExponent) + std::pow(std::fabs(y - T(1.0)), m_FinalExponent)), m_OneOverEx);
|
||||
}
|
||||
|
||||
T x1, y1;
|
||||
T r00 = fabs(r0 - T(0.5)) / m_Rmax;
|
||||
|
||||
if (r00 < 1)
|
||||
{
|
||||
x1 = 2 * (x + std::floor(helper.In.x));
|
||||
y1 = 2 * (y + std::floor(helper.In.y));
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = 0;
|
||||
y1 = 0;
|
||||
}
|
||||
|
||||
T r11 = std::fabs(r1 - T(0.5)) / m_Rmax;
|
||||
|
||||
if (r11 < 1)
|
||||
{
|
||||
helper.Out.x = x1 + (2 * (x + std::floor(helper.In.x))) - helper.In.x;
|
||||
helper.Out.y = y1 + (2 * (y + std::floor(helper.In.y))) - helper.In.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
helper.Out.x = x1 - helper.In.x;
|
||||
helper.Out.y = y1 - helper.In.y;
|
||||
}
|
||||
}
|
||||
|
||||
virtual string OpenCLString() const override
|
||||
{
|
||||
ostringstream ss, ss2;
|
||||
intmax_t i = 0;
|
||||
ss2 << "_" << XformIndexInEmber() << "]";
|
||||
string index = ss2.str();
|
||||
string exponent = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string arcWidth = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string seed = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string finalexponent = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string oneOverEx = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string width = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string seed2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string rmax = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string scale = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
ss << "\t{\n"
|
||||
<< "\t\treal_t modbase = 65535;\n"
|
||||
<< "\t\treal_t multiplier = 32747;\n"
|
||||
<< "\t\treal_t offset = 12345;\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t x = vIn.x * " << scale << ";\n"
|
||||
<< "\t\treal_t y = vIn.y * " << scale << ";\n"
|
||||
<< "\t\treal_t intx = Round(x);\n"
|
||||
<< "\t\treal_t inty = Round(y);\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t r = x - intx;\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (r < 0)\n"
|
||||
<< "\t\t\tx = r + 1;\n"
|
||||
<< "\t\telse\n"
|
||||
<< "\t\t\tx = r;\n"
|
||||
<< "\n"
|
||||
<< "\t\tr = y - inty;\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (r < 0)\n"
|
||||
<< "\t\t\ty = r + 1;\n"
|
||||
<< "\t\telse\n"
|
||||
<< "\t\t\ty = r;\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t tiletype = 0;\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (" << seed << " != 0)\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tif (" << seed << " == 1)\n"
|
||||
<< "\t\t\t{\n"
|
||||
<< "\t\t\t\ttiletype = " << seed << ";\n"
|
||||
<< "\t\t\t}\n"
|
||||
<< "\t\t\telse\n"
|
||||
<< "\t\t\t{\n"
|
||||
<< "\t\t\t\treal_t xrand = vIn.x;\n"
|
||||
<< "\t\t\t\treal_t yrand = vIn.y;\n"
|
||||
<< "\n"
|
||||
<< "\t\t\t\txrand = Round(fabs(xrand)) * " << seed2 << ";\n"
|
||||
<< "\t\t\t\tyrand = Round(fabs(yrand)) * " << seed2 << ";\n"
|
||||
<< "\n"
|
||||
<< "\t\t\t\treal_t niter = xrand + yrand + (xrand * yrand);\n"
|
||||
<< "\t\t\t\treal_t randint = (" << seed << " + niter) * " << seed2 << " * ((real_t) 0.5);\n"
|
||||
<< "\n"
|
||||
<< "\t\t\t\trandint = fmod((randint * multiplier + offset), modbase);\n"
|
||||
<< "\t\t\t\ttiletype = fmod(randint, 2);\n"
|
||||
<< "\t\t\t}\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t r0, r1;\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (tiletype < 1)\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tr0 = pow((pow(fabs(x), " << finalexponent << ") + pow(fabs(y), " << finalexponent << ")), " << oneOverEx << ");\n"
|
||||
<< "\t\t\tr1 = pow((pow(fabs(x-1), " << finalexponent << ") + pow(fabs(y-1), " << finalexponent << ")), " << oneOverEx << ");\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\t\telse\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tr0 = pow((pow(fabs(x-1), " << finalexponent << ") + pow(fabs(y), " << finalexponent << ")), " << oneOverEx << ");\n"
|
||||
<< "\t\t\tr1 = pow((pow(fabs(x), " << finalexponent << ") + pow(fabs(y-1), " << finalexponent << ")), " << oneOverEx << ");\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t x1, y1;\n"
|
||||
<< "\t\treal_t r00 = fabs(r0 - (real_t) 0.5) / " << rmax << ";\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (r00 < 1.0)\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tx1 = 2 * (x + floor(vIn.x));\n"
|
||||
<< "\t\t\ty1 = 2 * (y + floor(vIn.y));\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\t\telse\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tx1 = 0;\n"
|
||||
<< "\t\t\ty1 = 0;\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t r11 = fabs(r1 - (real_t) 0.5) / " << rmax << ";\n"
|
||||
<< "\n"
|
||||
<< "\t\tif (r11 < 1)\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tvOut.x = x1 + (2 * (x + floor(vIn.x))) - vIn.x;\n"
|
||||
<< "\t\t\tvOut.y = y1 + (2 * (y + floor(vIn.y))) - vIn.y;\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\t\telse\n"
|
||||
<< "\t\t{\n"
|
||||
<< "\t\t\tvOut.x = x1 - vIn.x;\n"
|
||||
<< "\t\t\tvOut.y = y1 - vIn.y;\n"
|
||||
<< "\t\t}\n"
|
||||
<< "\n"
|
||||
<< "\t\tvOut.z = " << DefaultZCl()
|
||||
<< "\t}\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
virtual vector<string> OpenCLGlobalFuncNames() const override
|
||||
{
|
||||
return vector<string> { "Round" };
|
||||
}
|
||||
|
||||
virtual void Precalc() override
|
||||
{
|
||||
m_FinalExponent = m_Exponent > T(2) ? T(2) : (m_Exponent < T(0.001) ? T(0.001) : m_Exponent);
|
||||
m_OneOverEx = T(1) / m_FinalExponent;
|
||||
m_Width = m_ArcWidth > T(1) ? T(1) : (m_ArcWidth < T(0.001) ? T(0.001) : m_ArcWidth);
|
||||
m_Seed2 = std::sqrt(m_Seed * T(1.5)) / (m_Seed * T(0.5)) * T(0.25);
|
||||
m_Rmax = T(0.5) * (std::pow(T(2), m_OneOverEx) - T(1)) * m_Width;
|
||||
m_Scale = T(1) / m_Weight;
|
||||
}
|
||||
|
||||
protected:
|
||||
void Init()
|
||||
{
|
||||
string prefix = Prefix();
|
||||
m_Params.clear();
|
||||
m_Params.push_back(ParamWithName<T>(&m_Exponent, prefix + "Truchet_fill_exponent", 2, eParamType::REAL_CYCLIC, T(0.001), 2));
|
||||
m_Params.push_back(ParamWithName<T>(&m_ArcWidth, prefix + "Truchet_fill_arc_width", T(0.5), eParamType::REAL_CYCLIC, T(0.001), 1));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Seed, prefix + "Truchet_fill_seed"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_FinalExponent, prefix + "Truchet_fill_final_exponent"));//Precalc
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_OneOverEx, prefix + "Truchet_fill_oneoverex"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Width, prefix + "Truchet_fill_width"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Seed2, prefix + "Truchet_fill_seed2"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Rmax, prefix + "Truchet_fill_rmax"));
|
||||
m_Params.push_back(ParamWithName<T>(true, &m_Scale, prefix + "Truchet_fill_scale"));
|
||||
}
|
||||
|
||||
private:
|
||||
T m_Exponent;
|
||||
T m_ArcWidth;
|
||||
T m_Seed;
|
||||
T m_FinalExponent;//Precalc.
|
||||
T m_OneOverEx;
|
||||
T m_Width;
|
||||
T m_Seed2;
|
||||
T m_Rmax;
|
||||
T m_Scale;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// waves2_radial.
|
||||
/// By tatasz.
|
||||
/// http://fav.me/dapecsh
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class Waves2RadialVariation : public ParametricVariation<T>
|
||||
{
|
||||
public:
|
||||
Waves2RadialVariation(T weight = 1.0) : ParametricVariation<T>("waves2_radial", eVariationId::VAR_WAVES2_RADIAL, weight, true, true)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
PARVARCOPY(Waves2RadialVariation)
|
||||
|
||||
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
{
|
||||
T x0 = helper.In.x;
|
||||
T y0 = helper.In.y;
|
||||
T dist = helper.m_PrecalcSqrtSumSquares;
|
||||
T factor = (dist < m_Distance) ? (dist - m_Null) / Zeps(m_Distance - m_Null) : T(1);
|
||||
factor = (dist < m_Null) ? T(0) : factor;
|
||||
helper.Out.x = m_Weight * (x0 + factor * std::sin(y0 * m_Freqx) * m_Scalex);
|
||||
helper.Out.y = m_Weight * (y0 + factor * std::sin(x0 * m_Freqy) * m_Scaley);
|
||||
}
|
||||
|
||||
virtual string OpenCLString() const override
|
||||
{
|
||||
ostringstream ss, ss2;
|
||||
intmax_t i = 0, varIndex = IndexInXform();
|
||||
ss2 << "_" << XformIndexInEmber() << "]";
|
||||
string index = ss2.str();
|
||||
string freqX = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string scaleX = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string freqY = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string scaleY = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string nullVar = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
string distance = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
|
||||
ss << "\t{\n"
|
||||
<< "\t\treal_t x0 = vIn.x;\n"
|
||||
<< "\t\treal_t y0 = vIn.y;\n"
|
||||
<< "\n"
|
||||
<< "\t\treal_t dist = precalcSqrtSumSquares;\n"
|
||||
<< "\t\treal_t factor = (dist < " << distance << ") ? (dist - " << nullVar << ") / Zeps(" << distance << "-" << nullVar << ") : (real_t)(1.0);\n"
|
||||
<< "\t\tfactor = (dist < " << nullVar << ") ? (real_t) 0.0 : factor;\n"
|
||||
<< "\n"
|
||||
<< "\t\tvOut.x = xform->m_VariationWeights[" << varIndex << "] * (x0 + factor * sin(y0 * " << freqX << ") * " << scaleX << ");\n"
|
||||
<< "\t\tvOut.y = xform->m_VariationWeights[" << varIndex << "] * (y0 + factor * sin(x0 * " << freqY << ") * " << scaleY << ");\n"
|
||||
<< "\t\tvOut.z = " << DefaultZCl()
|
||||
<< "\t}\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
virtual vector<string> OpenCLGlobalFuncNames() const override
|
||||
{
|
||||
return vector<string> { "Zeps" };
|
||||
}
|
||||
|
||||
protected:
|
||||
void Init()
|
||||
{
|
||||
string prefix = Prefix();
|
||||
m_Params.clear();
|
||||
m_Params.push_back(ParamWithName<T>(&m_Freqx, prefix + "waves2_radial_freqx", 7));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Scalex, prefix + "waves2_radial_scalex", T(0.1)));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Freqy, prefix + "waves2_radial_freqy", 13));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Scaley, prefix + "waves2_radial_scaley", T(0.1)));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Null, prefix + "waves2_radial_null", 2));
|
||||
m_Params.push_back(ParamWithName<T>(&m_Distance, prefix + "waves2_radial_distance", 10));
|
||||
}
|
||||
|
||||
private:
|
||||
T m_Freqx;
|
||||
T m_Scalex;
|
||||
T m_Freqy;
|
||||
T m_Scaley;
|
||||
T m_Null;
|
||||
T m_Distance;
|
||||
};
|
||||
|
||||
MAKEPREPOSTPARVAR(Splits3D, splits3D, SPLITS3D)
|
||||
MAKEPREPOSTPARVAR(Waves2B, waves2b, WAVES2B)
|
||||
MAKEPREPOSTPARVAR(JacCn, jac_cn, JAC_CN)
|
||||
@ -957,4 +1464,9 @@ MAKEPREPOSTPARVAR(PressureWave, pressure_wave, PRESSURE_WAVE)
|
||||
MAKEPREPOSTVAR(Gamma, gamma, GAMMA)
|
||||
MAKEPREPOSTPARVAR(PRose3D, pRose3D, PROSE3D)
|
||||
MAKEPREPOSTPARVAR(LogDB, log_db, LOG_DB)
|
||||
MAKEPREPOSTPARVAR(CircleSplit, circlesplit, CIRCLESPLIT)
|
||||
MAKEPREPOSTVAR(Cylinder2, cylinder2, CYLINDER2)
|
||||
MAKEPREPOSTPARVAR(TileLog, tile_log, TILE_LOG)
|
||||
MAKEPREPOSTPARVAR(TruchetFill, Truchet_fill, TRUCHET_FILL)
|
||||
MAKEPREPOSTPARVAR(Waves2Radial, waves2_radial, WAVES2_RADIAL)
|
||||
}
|
||||
|
1456
Source/Ember/XmlToEmber.cpp
Normal file
1456
Source/Ember/XmlToEmber.cpp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user