From 1a37504b0956ee428516c67383ebb21ce504d32d Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Mon, 22 Jun 2015 14:50:43 +0100 Subject: [PATCH 01/10] Add 'saw' motion function --- Source/Ember/EmberDefines.h | 2 +- Source/Ember/EmberToXml.h | 2 ++ Source/Ember/Interpolate.h | 6 +++++- Source/Ember/XmlToEmber.h | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index 534f3a6..cd619be 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -114,7 +114,7 @@ enum eInterp : uint { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 }; enum eAffineInterp : uint { INTERP_LINEAR = 0, INTERP_LOG = 1, INTERP_COMPAT = 2, INTERP_OLDER = 3 }; enum ePaletteMode : uint { PALETTE_STEP = 0, PALETTE_LINEAR = 1 }; enum ePaletteInterp : uint { INTERP_HSV = 0, INTERP_SWEEP = 1 }; -enum eMotion : uint { MOTION_SIN = 1, MOTION_TRIANGLE = 2, MOTION_HILL = 3 }; +enum eMotion : uint { MOTION_SIN = 1, MOTION_TRIANGLE = 2, MOTION_HILL = 3, MOTION_SAW = 4 }; enum eProcessAction : uint { NOTHING = 0, ACCUM_ONLY = 1, FILTER_AND_ACCUM = 2, KEEP_ITERATING = 3, FULL_RENDER = 4 }; enum eProcessState : uint { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DONE = 3, ACCUM_DONE = 4 }; enum eInteractiveFilter : uint { FILTER_LOG = 0, FILTER_DE = 1 }; diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index 9d6cbf1..b1febdd 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -488,6 +488,8 @@ private: os << "motion_function=\"triangle\" "; else if (xform.m_MotionFunc== MOTION_HILL) os << "motion_function=\"hill\" "; + else if (xform.m_MotionFunc== MOTION_SAW) + os << "motion_function=\"saw\" "; } else { diff --git a/Source/Ember/Interpolate.h b/Source/Ember/Interpolate.h index 23b16d2..950ca86 100644 --- a/Source/Ember/Interpolate.h +++ b/Source/Ember/Interpolate.h @@ -883,10 +883,14 @@ public: return fr; } - else//MOTION_HILL + else if (funcNum == MOTION_HILL) { return ((1 - cos(T(2.0) * T(M_PI) * timeVal)) * T(0.5)); } + else //MOTION_SAW + { + return (T(2.0) * fmod(timeVal - T(0.5), T(1.0)) - T(1.0)); + } } /* diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 07ea62f..0a92ab4 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -1073,6 +1073,8 @@ private: xform.m_MotionFunc = MOTION_TRIANGLE; else if (!_stricmp("hill", attStr)) xform.m_MotionFunc = MOTION_HILL; + else if (!_stricmp("saw", attStr)) + xform.m_MotionFunc = MOTION_SAW; else { xform.m_MotionFunc = MOTION_SIN; From d75a15136d6214d698c8c00deca47250c2537431 Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Wed, 24 Jun 2015 10:52:21 +0100 Subject: [PATCH 02/10] Convert motion_frequency to floating point --- Source/Ember/Xform.h | 4 ++-- Source/Ember/XmlToEmber.h | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Ember/Xform.h b/Source/Ember/Xform.h index 4e94319..cffc862 100644 --- a/Source/Ember/Xform.h +++ b/Source/Ember/Xform.h @@ -731,7 +731,7 @@ public: { //Original only pulls these from the first motion xform which is a bug. Want to pull it from each one. Xform& currentMot = xform.m_Motion[i]; - intmax_t freq = currentMot.m_MotionFreq; + T freq = currentMot.m_MotionFreq; eMotion func = currentMot.m_MotionFunc; //Clamp these to the appropriate range after all are applied. @@ -1232,7 +1232,7 @@ public: T m_Animate;//Whether or not this xform rotates during animation. 0 means stationary, > 0 means rotate. Use T instead of bool so it can be interpolated. T m_Wind[2]; eMotion m_MotionFunc; - intmax_t m_MotionFreq; + T m_MotionFreq; vector> m_Motion; string m_Name; diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 0a92ab4..8293598 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -971,7 +971,7 @@ private: if (theXform) { //Check for non-zero motion params. - if (theXform->m_MotionFreq != 0)//Original checked for motion func being non-zero, but it was set to MOTION_SIN (1) in Xform::Init(), so don't check for 0 here. + if (abs(theXform->m_MotionFreq) > 0.0)//Original checked for motion func being non-zero, but it was set to MOTION_SIN (1) in Xform::Init(), so don't check for 0 here. { m_ErrorReport.push_back(string(loc) + " : Motion parameters should not be specified in regular, non-motion xforms"); } @@ -1047,9 +1047,7 @@ private: else if (ParseAndAssignFloat(curAtt->name, attStr, "animate", xform.m_Animate, success)) { } else if (ParseAndAssignFloat(curAtt->name, attStr, "opacity", xform.m_Opacity, success)) { } else if (ParseAndAssignFloat(curAtt->name, attStr, "var_color", xform.m_DirectColor, success)) { } - - //Parse simple int reads. - else if (ParseAndAssignInt(curAtt->name, attStr, "motion_frequency", xform.m_MotionFreq, success)) { } + else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", xform.m_MotionFreq, success)) { } //Parse more complicated reads that have multiple possible values. else if (!Compare(curAtt->name, "name")) From 9d5f3e85784dd804ad371708e1a4dd25124cfefc Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Wed, 24 Jun 2015 11:23:17 +0100 Subject: [PATCH 03/10] Add new class to store flame motion parameters --- Builds/QtCreator/Ember/Ember.pro | 3 +- Source/Ember/Ember.h | 9 +++++ Source/Ember/EmberDefines.h | 6 +++ Source/Ember/FlameMotion.h | 63 ++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 Source/Ember/FlameMotion.h diff --git a/Builds/QtCreator/Ember/Ember.pro b/Builds/QtCreator/Ember/Ember.pro index 58cf2c0..b0e57e1 100644 --- a/Builds/QtCreator/Ember/Ember.pro +++ b/Builds/QtCreator/Ember/Ember.pro @@ -52,5 +52,6 @@ HEADERS += \ ../../../Source/Ember/Variations05.h \ ../../../Source/Ember/VariationsDC.h \ ../../../Source/Ember/Xform.h \ - ../../../Source/Ember/XmlToEmber.h + ../../../Source/Ember/XmlToEmber.h \ + ../../../Source/Ember/FlameMotion.h diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index da0726e..06426ce 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -5,6 +5,7 @@ #include "PaletteList.h" #include "SpatialFilter.h" #include "TemporalFilter.h" +#include "FlameMotion.h" /// /// Ember class. @@ -183,6 +184,11 @@ public: if (ember.m_Edits != nullptr) m_Edits = xmlCopyDoc(ember.m_Edits, 1); + m_FlameMotionElements.clear(); + + for(int i = 0; i < ember.m_FlameMotionElements.size(); ++i) + m_FlameMotionElements.push_back(ember.m_FlameMotionElements[i]); + return *this; } @@ -1683,6 +1689,9 @@ public: //The 0-based position of this ember in the file it was contained in. size_t m_Index; + //The list of motion elements for the top-level flame params + vector> m_FlameMotionElements; + private: /// /// The type of scaling used when resizing. diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index cd619be..92e69ae 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -120,4 +120,10 @@ enum eProcessState : uint { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DO enum eInteractiveFilter : uint { FILTER_LOG = 0, FILTER_DE = 1 }; enum eScaleType : uint { SCALE_NONE = 0, SCALE_WIDTH = 1, SCALE_HEIGHT = 2 }; enum eRenderStatus : uint { RENDER_OK = 0, RENDER_ERROR = 1, RENDER_ABORT = 2 }; +enum eFlameMotionParam : uint { + FLAME_MOTION_NONE = 0, FLAME_MOTION_ZOOM = 1, FLAME_MOTION_ZPOS = 2, FLAME_MOTION_PERSPECTIVE = 3, FLAME_MOTION_YAW = 4, FLAME_MOTION_PITCH = 5, FLAME_MOTION_DEPTH_BLUR = 6, + FLAME_MOTION_CENTER_X = 7, FLAME_MOTION_CENTER_Y = 8, FLAME_MOTION_ROTATE = 9, FLAME_MOTION_HUE = 10, FLAME_MOTION_BRIGHTNESS = 11, FLAME_MOTION_GAMMA = 12, + FLAME_MOTION_GAMMA_THRESH = 13, FLAME_MOTION_HIGHLIGHT_POWER = 14, FLAME_MOTION_BACKGROUND_R = 15, FLAME_MOTION_BACKGROUND_G = 16, + FLAME_MOTION_BACKGROUND_B = 17, FLAME_MOTION_VIBRANCY = 18 +}; } diff --git a/Source/Ember/FlameMotion.h b/Source/Ember/FlameMotion.h new file mode 100644 index 0000000..6700f42 --- /dev/null +++ b/Source/Ember/FlameMotion.h @@ -0,0 +1,63 @@ +#pragma once + +#include "EmberDefines.h" + +namespace EmberNs +{ + +/// +/// FlameMotion elements allow for motion of the flame parameters such as zoom, yaw, pitch and friends +/// The values in these elements can be used to modify flame parameters during rotation in much the same +/// way as motion elements on xforms do. +/// Template argument expected to be float or double. +/// +template +class EMBER_API FlameMotion +{ +public: + + FlameMotion() + { + m_MotionFreq = 0; + m_MotionFunc = MOTION_SIN; + } + + FlameMotion(const FlameMotion &other) + { + operator=(other); + } + + template + FlameMotion(const FlameMotion &other) + { + operator=(other); + } + + FlameMotion& operator = (const FlameMotion& other) + { + if (this != &other) + FlameMotion::operator=(other); + + return *this; + } + + template + FlameMotion &operator = (const FlameMotion &other) + { + m_MotionParams.clear(); + + for (int i = 0; i < other.m_MotionParams.size(); ++i) + m_MotionParams.push_back(pair(other.m_MotionParams[i].first, T(other.m_MotionParams[i].second))); + + m_MotionFunc = other.m_MotionFunc; + m_MotionFreq = T(other.m_MotionFreq); + return *this; + } + + T m_MotionFreq; + eMotion m_MotionFunc; + vector> m_MotionParams; + +}; + +} From 679a8b4129041171d76096d947043cdc654777b4 Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Fri, 26 Jun 2015 21:23:27 +0100 Subject: [PATCH 04/10] Serialise FlameMotion elements to/from XML --- Source/Ember/EmberToXml.h | 107 ++++++++++++++++++++++++++++++ Source/Ember/XmlToEmber.h | 132 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index b1febdd..45a2810 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -221,6 +221,9 @@ public: os << "\">\n"; + for (i = 0; i < ember.m_FlameMotionElements.size(); ++i) + os << " " << ToString(ember.m_FlameMotionElements[i]); + //This is a grey area, what to do about symmetry to avoid duplicating the symmetry xforms when reading back?//TODO//BUG. //if (ember.m_Symmetry) // os << " \n"; @@ -719,6 +722,110 @@ private: return os.str(); } + /// + /// Convert a FlameMotion element to an xml string + /// + /// The FlameMotion object to convert to XML + string ToString(const FlameMotion &motion) + { + ostringstream os; + os << "\n"; + + return os.str(); + } + void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename) { if (filename.find_first_of('&') != std::string::npos) diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 8293598..290b43e 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -998,6 +998,112 @@ private: editNode = xmlCopyNode(childNode, 1); xmlDocSetRootElement(currentEmber.m_Edits, editNode); } + else if (!Compare(childNode->name, "flame_motion")) + { + FlameMotion motion; + + att = childNode->properties; + + if (att == nullptr) + { + m_ErrorReport.push_back(string(loc) + " : element has no attributes"); + return false; + } + + for (curAtt = att; curAtt; curAtt = curAtt->next) + { + attStr = reinterpret_cast(xmlGetProp(childNode, curAtt->name)); + + if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", motion.m_MotionFreq, ret)) { } + else if (!Compare(curAtt->name, "motion_function")) + { + string func(attStr); + if ( func == "sin" ) + motion.m_MotionFunc = MOTION_SIN; + else if ( func == "triangle" ) + motion.m_MotionFunc = MOTION_TRIANGLE; + else if ( func == "hill" ) + motion.m_MotionFunc = MOTION_HILL; + else if ( func == "saw" ) + motion.m_MotionFunc = MOTION_SAW; + else + { + m_ErrorReport.push_back(string(loc) + " : invalid flame motion function " + func); + return false; + } + } + else if (!Compare(curAtt->name, "zoom")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ZOOM, motion); + else if (!Compare(curAtt->name, "cam_zpos")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ZPOS, motion); + else if (!Compare(curAtt->name, "cam_persp")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_PERSPECTIVE, motion); + else if (!Compare(curAtt->name, "cam_yaw")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_YAW, motion); + else if (!Compare(curAtt->name, "cam_pitch")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_PITCH, motion); + else if (!Compare(curAtt->name, "cam_dof")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_DEPTH_BLUR, motion); + else if (!Compare(curAtt->name, "rotate")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ROTATE, motion); + else if (!Compare(curAtt->name, "hue")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_HUE, motion); + else if (!Compare(curAtt->name, "brightness")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_BRIGHTNESS, motion); + else if (!Compare(curAtt->name, "gamma")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_GAMMA, motion); + else if (!Compare(curAtt->name, "gamma_threshold")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_GAMMA_THRESH, motion); + else if (!Compare(curAtt->name, "highlight_power")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_HIGHLIGHT_POWER, motion); + else if (!Compare(curAtt->name, "vibrancy")) + ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_VIBRANCY, motion); + else if (!Compare(curAtt->name, "background")) + { + double r, g, b; + + if (sscanf_s(attStr, "%lf %lf %lf", &r, &g, &b) != 3) + { + m_ErrorReport.push_back(string(loc) + " : Invalid flame motion background attribute " + string(attStr)); + xmlFree(attStr); + return false; + } + + if (r != 0) + motion.m_MotionParams.push_back(pair(FLAME_MOTION_BACKGROUND_R, T(r))); + if (g != 0) + motion.m_MotionParams.push_back(pair(FLAME_MOTION_BACKGROUND_G, T(g))); + if (b != 0) + motion.m_MotionParams.push_back(pair(FLAME_MOTION_BACKGROUND_B, T(b))); + } + else if (!Compare(curAtt->name, "center")) + { + double cx, cy; + + if (sscanf_s(attStr, "%lf %lf", &cx, &cy) != 2) + { + m_ErrorReport.push_back(string(loc) + " : Invalid flame motion center attribute " + string(attStr)); + xmlFree(attStr); + return false; + } + + if (cx != 0) + motion.m_MotionParams.push_back(pair(FLAME_MOTION_CENTER_X, T(cx))); + if (cy != 0) + motion.m_MotionParams.push_back(pair(FLAME_MOTION_CENTER_Y, T(cy))); + } + else + { + m_ErrorReport.push_back(string(loc) + " : Unknown flame motion attribute " + string(CCX(curAtt->name))); + xmlFree(attStr); + return false; + } + + xmlFree(attStr); + } + + currentEmber.m_FlameMotionElements.push_back(motion); + } } //if (!newLinear) @@ -1010,6 +1116,32 @@ private: return m_ErrorReport.empty(); } + /// + /// Parse a floating point value from an xml attribute and add the value to a FlameMotion object + /// + /// The current attribute + /// The attribute value to parse + /// The flame motion parameter type + /// The flame motion element to add the parameter to + /// True if there were no errors, else false. + bool AttToFlameMotionFloat(xmlAttrPtr att, const char *attStr, eFlameMotionParam param, FlameMotion &motion) + { + const char* loc = __FUNCTION__; + + bool r = false; + T val = 0.0; + + if (Atof(attStr, val)) + { + motion.m_MotionParams.push_back(pair(param, val)); + r = true; + } else { + m_ErrorReport.push_back(string(loc) + " : Failed to parse float value for flame motion attribute \"" + string(CCX(att->name)) + "\" : " + string(attStr) + ""); + } + + return r; + } + /// /// Parse an xform element. /// From f606986b7d4e3396343899412d944214973d4caf Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Thu, 2 Jul 2015 18:01:37 +0100 Subject: [PATCH 05/10] Apply flame motion elements when generating flame sequence for animation --- Source/Ember/Ember.h | 78 +++++++++++++++++++++++++++++++++++++++ Source/Ember/SheepTools.h | 6 ++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index 06426ce..de18c0f 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -478,6 +478,8 @@ public: { for (size_t i = 0; i < TotalXformCount(); i++) GetTotalXform(i)->DeleteMotionElements(); + + m_FlameMotionElements.clear(); } /// @@ -1291,6 +1293,82 @@ public: point.m_Z -= m_CamZPos; } +#define APP_FMP(x) x += param.second * Interpolater::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * blend) + /// + /// Update ember parameters based on stored motion elements + /// + /// The time percentage value which dictates how much of a percentage of 360 degrees it should be rotated and the time position for the motion elements + void ApplyFlameMotion(T blend) + { + for (size_t i = 0; i < m_FlameMotionElements.size(); ++i) + { + const FlameMotion &motion = m_FlameMotionElements[i]; + + for (size_t j = 0; j < motion.m_MotionParams.size(); ++j) + { + const pair ¶m = motion.m_MotionParams[j]; + + switch (param.first) + { + case FLAME_MOTION_ZOOM: + APP_FMP(m_Zoom); + break; + case FLAME_MOTION_ZPOS: + APP_FMP(m_CamZPos); + break; + case FLAME_MOTION_PERSPECTIVE: + APP_FMP(m_CamPerspective); + break; + case FLAME_MOTION_YAW: + APP_FMP(m_CamYaw); + break; + case FLAME_MOTION_PITCH: + APP_FMP(m_CamPitch); + break; + case FLAME_MOTION_DEPTH_BLUR: + APP_FMP(m_CamDepthBlur); + break; + case FLAME_MOTION_CENTER_X: + APP_FMP(m_CenterX); + break; + case FLAME_MOTION_CENTER_Y: + APP_FMP(m_CenterY); + break; + case FLAME_MOTION_ROTATE: + APP_FMP(m_Rotate); + break; + case FLAME_MOTION_HUE: + APP_FMP(m_Hue); + break; + case FLAME_MOTION_BRIGHTNESS: + APP_FMP(m_Brightness); + break; + case FLAME_MOTION_GAMMA: + APP_FMP(m_Gamma); + break; + case FLAME_MOTION_GAMMA_THRESH: + APP_FMP(m_GammaThresh); + break; + case FLAME_MOTION_HIGHLIGHT_POWER: + APP_FMP(m_HighlightPower); + break; + case FLAME_MOTION_BACKGROUND_R: + APP_FMP(m_Background.r); + break; + case FLAME_MOTION_BACKGROUND_G: + APP_FMP(m_Background.g); + break; + case FLAME_MOTION_BACKGROUND_B: + APP_FMP(m_Background.b); + break; + case FLAME_MOTION_VIBRANCY: + APP_FMP(m_Vibrancy); + break; + } + } + } + } + /// /// Clear this ember and set to either reasonable or unreasonable default values. /// diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h index d1d8c58..b01ac14 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -1010,11 +1010,13 @@ public: if (!xform1->m_Motion.empty()) xform2->ApplyMotion(*xform1, blend); - - xform2->DeleteMotionElements(); } + rotated.ApplyFlameMotion(blend); + rotated.RotateAffines(-blend * 360);//Rotate the affines. + + rotated.DeleteMotionElements(); // delete all motion elements from the looped flame } /// From efb39f81601230883c499288ed139298f4167b7b Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Mon, 6 Jul 2015 12:06:20 +0100 Subject: [PATCH 06/10] Make 'loops' param to EmberGenome work correcly, and convert to double --- Source/EmberCommon/EmberOptions.h | 8 ++++---- Source/EmberGenome/EmberGenome.cpp | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Source/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h index 047af08..fc7e453 100644 --- a/Source/EmberCommon/EmberOptions.h +++ b/Source/EmberCommon/EmberOptions.h @@ -76,7 +76,6 @@ enum eOptionIDs OPT_SYMMETRY, OPT_SHEEP_GEN, OPT_SHEEP_ID, - OPT_LOOPS, OPT_REPEAT, OPT_TRIES, OPT_MAX_XFORMS, @@ -93,6 +92,7 @@ enum eOptionIDs OPT_OFFSETX, OPT_OFFSETY, OPT_USEMEM, + OPT_LOOPS, OPT_ISAAC_SEED,//String value args. OPT_IN, @@ -357,7 +357,6 @@ public: INITUINTOPTION(Frame, Eou(OPT_ANIM_GENOME, OPT_FRAME, _T("--frame"), 0, SO_REQ_SEP, "\t--frame= Synonym for \"time\".\n")); INITUINTOPTION(Dtime, Eou(OPT_USE_ANIMATE, OPT_DTIME, _T("--dtime"), 1, SO_REQ_SEP, "\t--dtime= Time between frames [default: 1].\n")); INITUINTOPTION(Frames, Eou(OPT_USE_GENOME, OPT_NFRAMES, _T("--nframes"), 20, SO_REQ_SEP, "\t--nframes= Number of frames for each stage of the animation [default: 20].\n")); - INITUINTOPTION(Loops, Eou(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops= Number of times to rotate each control point in sequence [default: 1].\n")); INITUINTOPTION(Repeat, Eou(OPT_USE_GENOME, OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat= Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n")); INITUINTOPTION(Tries, Eou(OPT_USE_GENOME, OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries= Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n")); INITUINTOPTION(MaxXforms, Eou(OPT_USE_GENOME, OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms= The maximum number of xforms allowed in the final output.\n")); @@ -376,6 +375,7 @@ public: INITDOUBLEOPTION(OffsetX, Eod(OPT_USE_GENOME, OPT_OFFSETX, _T("--offsetx"), 0.0, SO_REQ_SEP, "\t--offsetx= Amount to jitter each flame horizontally when applying genome tools [default: 0].\n")); INITDOUBLEOPTION(OffsetY, Eod(OPT_USE_GENOME, OPT_OFFSETY, _T("--offsety"), 0.0, SO_REQ_SEP, "\t--offsety= Amount to jitter each flame vertically when applying genome tools [default: 0].\n")); INITDOUBLEOPTION(UseMem, Eod(OPT_USE_RENDER, OPT_USEMEM, _T("--use_mem"), 0.0, SO_REQ_SEP, "\t--use_mem= Number of bytes of memory to use [default: max system memory].\n")); + INITDOUBLEOPTION(Loops, Eod(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops= Number of times to rotate each control point in sequence [default: 1].\n")); //String. INITSTRINGOPTION(IsaacSeed, Eos(OPT_USE_ALL, OPT_ISAAC_SEED, _T("--isaac_seed"), "", SO_REQ_SEP, "\t--isaac_seed= Character-based seed for the random number generator [default: random].\n")); @@ -487,7 +487,6 @@ public: PARSEUINTOPTION(OPT_TIME, Time); PARSEUINTOPTION(OPT_DTIME, Dtime); PARSEUINTOPTION(OPT_NFRAMES, Frames); - PARSEUINTOPTION(OPT_LOOPS, Loops); PARSEUINTOPTION(OPT_REPEAT, Repeat); PARSEUINTOPTION(OPT_TRIES, Tries); PARSEUINTOPTION(OPT_MAX_XFORMS, MaxXforms); @@ -503,6 +502,7 @@ public: PARSEDOUBLEOPTION(OPT_OFFSETX, OffsetX); PARSEDOUBLEOPTION(OPT_OFFSETY, OffsetY); PARSEDOUBLEOPTION(OPT_USEMEM, UseMem); + PARSEDOUBLEOPTION(OPT_LOOPS, Loops); PARSESTRINGOPTION(OPT_ISAAC_SEED, IsaacSeed);//String args. PARSESTRINGOPTION(OPT_IN, Input); @@ -703,7 +703,6 @@ public: EmberOptionEntry Time; EmberOptionEntry Dtime; EmberOptionEntry Frames; - EmberOptionEntry Loops; EmberOptionEntry Repeat; EmberOptionEntry Tries; EmberOptionEntry MaxXforms; @@ -719,6 +718,7 @@ public: EmberOptionEntry OffsetX; EmberOptionEntry OffsetY; EmberOptionEntry UseMem; + EmberOptionEntry Loops; EmberOptionEntry IsaacSeed;//Value string. EmberOptionEntry Input; diff --git a/Source/EmberGenome/EmberGenome.cpp b/Source/EmberGenome/EmberGenome.cpp index 4292f82..a761393 100644 --- a/Source/EmberGenome/EmberGenome.cpp +++ b/Source/EmberGenome/EmberGenome.cpp @@ -382,9 +382,9 @@ bool EmberGenome(EmberOptions& opt) for (i = 0; i < embers.size(); i++) { - if (opt.Loops()) + if (opt.Loops() > 0) { - for (frame = 0; frame < opt.Frames(); frame++) + for (frame = 0; frame < round(T(opt.Frames()) * opt.Loops()); frame++) { blend = T(frame) / T(opt.Frames()); tools.Spin(embers[i], pTemplate, result, frameCount++, blend);//Result is cleared and reassigned each time inside of Spin(). @@ -394,12 +394,23 @@ bool EmberGenome(EmberOptions& opt) if (i < embers.size() - 1) { + vector> interpEmbers; + interpEmbers.push_back(embers[i]); + interpEmbers.push_back(embers[i + 1]); + + if (opt.Loops() > 0) + { + // we might have looped a non-integral number of times, so store the last result as our flame to interpolate from + + interpEmbers[i] = result; + } + for (frame = 0; frame < opt.Frames(); frame++) { seqFlag = (frame == 0 || frame == opt.Frames() - 1); blend = frame / T(opt.Frames()); result.Clear(); - tools.SpinInter(&embers[i], pTemplate, result, frameCount++, seqFlag, blend); + tools.SpinInter(&interpEmbers[i], pTemplate, result, frameCount++, seqFlag, blend); cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), false, opt.HexPalette()); } } From 3fa59be990bff27ea6e2d6d0b14eda99da6c04ca Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Mon, 6 Jul 2015 15:05:43 +0100 Subject: [PATCH 07/10] Add new 'motion_offset' parameter to motion elements --- Source/Ember/Ember.h | 2 +- Source/Ember/EmberToXml.h | 6 ++++++ Source/Ember/FlameMotion.h | 3 +++ Source/Ember/Xform.h | 14 ++++++++++---- Source/Ember/XmlToEmber.h | 2 ++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index de18c0f..b02cd76 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -1293,7 +1293,7 @@ public: point.m_Z -= m_CamZPos; } -#define APP_FMP(x) x += param.second * Interpolater::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * blend) +#define APP_FMP(x) x += param.second * Interpolater::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * (blend + motion.m_MotionOffset)) /// /// Update ember parameters based on stored motion elements /// diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index 45a2810..f79ba8a 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -493,6 +493,9 @@ private: os << "motion_function=\"hill\" "; else if (xform.m_MotionFunc== MOTION_SAW) os << "motion_function=\"saw\" "; + + if (xform.m_MotionOffset != 0) + os << "motion_offset=\"" << xform.m_MotionOffset << "\" "; } else { @@ -731,6 +734,9 @@ private: ostringstream os; os << " 0) + os << "motion_offset=\"" << motion.m_MotionOffset << "\" "; + os << "motion_func="; switch(motion.m_MotionFunc) { diff --git a/Source/Ember/FlameMotion.h b/Source/Ember/FlameMotion.h index 6700f42..7f47d90 100644 --- a/Source/Ember/FlameMotion.h +++ b/Source/Ember/FlameMotion.h @@ -20,6 +20,7 @@ public: { m_MotionFreq = 0; m_MotionFunc = MOTION_SIN; + m_MotionOffset = 0; } FlameMotion(const FlameMotion &other) @@ -51,10 +52,12 @@ public: m_MotionFunc = other.m_MotionFunc; m_MotionFreq = T(other.m_MotionFreq); + m_MotionOffset = T(other.m_MotionOffset); return *this; } T m_MotionFreq; + T m_MotionOffset; eMotion m_MotionFunc; vector> m_MotionParams; diff --git a/Source/Ember/Xform.h b/Source/Ember/Xform.h index cffc862..f36edec 100644 --- a/Source/Ember/Xform.h +++ b/Source/Ember/Xform.h @@ -165,6 +165,7 @@ public: m_Wind[1] = T(xform.m_Wind[1]); m_MotionFreq = xform.m_MotionFreq; m_MotionFunc = xform.m_MotionFunc; + m_MotionOffset = xform.m_MotionOffset; ClearAndDeleteVariations(); @@ -234,6 +235,7 @@ public: m_Wind[0] = 0; m_Wind[1] = 0; m_MotionFreq = 0; + m_MotionOffset = 0; } else { @@ -262,6 +264,7 @@ public: m_Wind[0] = EMPTYFIELD; m_Wind[1] = EMPTYFIELD; m_MotionFreq = EMPTYFIELD; + m_MotionOffset = EMPTYFIELD; } m_MotionFunc = MOTION_SIN; @@ -716,7 +719,7 @@ public: do \ { \ if (currentMot.x != EMPTYFIELD) \ - x += currentMot.x * Interpolater::MotionFuncs(func, freq * blend); \ + x += currentMot.x * Interpolater::MotionFuncs(func, freq * (blend + offset)); \ } while (0) /// @@ -733,6 +736,7 @@ public: Xform& currentMot = xform.m_Motion[i]; T freq = currentMot.m_MotionFreq; eMotion func = currentMot.m_MotionFunc; + T offset = currentMot.m_MotionOffset; //Clamp these to the appropriate range after all are applied. APPMOT(m_Weight); @@ -753,13 +757,13 @@ public: if (!var)//It wasn't present, so add it and set the weight. { Variation* newVar = motVar->Copy(); - newVar->m_Weight = motVar->m_Weight * Interpolater::MotionFuncs(func, freq * blend); + newVar->m_Weight = motVar->m_Weight * Interpolater::MotionFuncs(func, freq * (blend + offset)); AddVariation(newVar); var = newVar;//Use this below for params. } else//It was present, so apply the motion func to the weight. { - var->m_Weight += motVar->m_Weight * Interpolater::MotionFuncs(func, freq * blend); + var->m_Weight += motVar->m_Weight * Interpolater::MotionFuncs(func, freq * (blend + offset)); } //At this point, we've added if needed, or just applied the motion func to the weight. @@ -773,7 +777,7 @@ public: for (size_t k = 0; k < motParVar->ParamCount(); k++) { if (!motParams[k].IsPrecalc()) - *(params[k].Param()) += motParams[k].ParamVal() * Interpolater::MotionFuncs(func, freq * blend); + *(params[k].Param()) += motParams[k].ParamVal() * Interpolater::MotionFuncs(func, freq * (blend + offset)); } } } @@ -1157,6 +1161,7 @@ public: ss << "Wind: " << m_Wind[0] << ", " << m_Wind[1] << endl; ss << "Motion Frequency: " << m_MotionFreq << endl; ss << "Motion Func: " << m_MotionFunc << endl; + ss << "Motion Offset: " << m_MotionOffset << endl; const_cast*>(this)->AllVarsFunc([&] (vector*>& variations, bool& keepGoing) { @@ -1233,6 +1238,7 @@ public: T m_Wind[2]; eMotion m_MotionFunc; T m_MotionFreq; + T m_MotionOffset; vector> m_Motion; string m_Name; diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 290b43e..b823ed0 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -1015,6 +1015,7 @@ private: attStr = reinterpret_cast(xmlGetProp(childNode, curAtt->name)); if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", motion.m_MotionFreq, ret)) { } + else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_offset", motion.m_MotionOffset, ret)) { } else if (!Compare(curAtt->name, "motion_function")) { string func(attStr); @@ -1180,6 +1181,7 @@ private: else if (ParseAndAssignFloat(curAtt->name, attStr, "opacity", xform.m_Opacity, success)) { } else if (ParseAndAssignFloat(curAtt->name, attStr, "var_color", xform.m_DirectColor, success)) { } else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", xform.m_MotionFreq, success)) { } + else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_offset", xform.m_MotionOffset, success)) { } //Parse more complicated reads that have multiple possible values. else if (!Compare(curAtt->name, "name")) From 570331adb5be4f3d7382f5b05faafdf7195ea21d Mon Sep 17 00:00:00 2001 From: mfeemster Date: Mon, 6 Jul 2015 20:04:38 -0700 Subject: [PATCH 08/10] --Bug fixes -Allow for empty fields in template files. --Code changes -When parsing, a boolean is not available to specify whether to use the default values if they are not present. This will be false for template files. --- Source/Ember/XmlToEmber.h | 19 +++++++++++++------ Source/EmberCommon/EmberCommon.h | 5 +++-- Source/EmberGenome/EmberGenome.cpp | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 07ea62f..253dcd2 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -253,8 +253,9 @@ public: /// The buffer to parse /// Full path and filename, optionally empty /// The newly constructed embers based on what was parsed + /// True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present. Default: true. /// True if there were no errors, else false. - bool Parse(byte* buf, const char* filename, vector>& embers) + bool Parse(byte* buf, const char* filename, vector>& embers, bool useDefaults = true) { char* bn; const char* xmlPtr; @@ -286,7 +287,7 @@ public: //Scan for nodes, starting with this node. //t.Tic(); bn = basename(const_cast(filename)); - ScanForEmberNodes(rootnode, bn, embers); + ScanForEmberNodes(rootnode, bn, embers, useDefaults); xmlFreeDoc(doc); emberSize = embers.size(); //t.Toc("ScanForEmberNodes"); @@ -337,8 +338,9 @@ public: /// /// Full path and filename /// The newly constructed embers based on what was parsed + /// True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present. Default: true. /// True if there were no errors, else false. - bool Parse(const char* filename, vector>& embers) + bool Parse(const char* filename, vector>& embers, bool useDefaults = true) { const char* loc = __FUNCTION__; string buf; @@ -353,7 +355,7 @@ public: if (ReadFile(filename, buf)) { std::replace(buf.begin(), buf.end(), '&', '+'); - return Parse(reinterpret_cast(const_cast(buf.data())), filename, embers); + return Parse(reinterpret_cast(const_cast(buf.data())), filename, embers, useDefaults); } else return false; @@ -488,7 +490,8 @@ private: /// The current node to parse /// The full path and filename /// The newly constructed embers based on what was parsed - void ScanForEmberNodes(xmlNode* curNode, char* parentFile, vector>& embers) + /// True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present. + void ScanForEmberNodes(xmlNode* curNode, char* parentFile, vector>& embers, bool useDefaults) { bool parseEmberSuccess; xmlNodePtr thisNode = nullptr; @@ -504,6 +507,10 @@ private: { Ember currentEmber;//Place this inside here so its constructor is called each time. + //Useful for parsing templates when not every member should be set. + if (!useDefaults) + currentEmber.Clear(false); + parseEmberSuccess = ParseEmberElement(thisNode, currentEmber); if (!parseEmberSuccess) @@ -532,7 +539,7 @@ private: else { //Check all of the children of this element. - ScanForEmberNodes(thisNode->children, parentFile, embers); + ScanForEmberNodes(thisNode->children, parentFile, embers, useDefaults); } } } diff --git a/Source/EmberCommon/EmberCommon.h b/Source/EmberCommon/EmberCommon.h index 69926eb..a96f12a 100644 --- a/Source/EmberCommon/EmberCommon.h +++ b/Source/EmberCommon/EmberCommon.h @@ -81,11 +81,12 @@ private: /// The parser to use /// The full path and name of the file /// Storage for the embers read from the file +/// True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present. Default: true. /// True if success, else false. template -static bool ParseEmberFile(XmlToEmber& parser, string filename, vector>& embers) +static bool ParseEmberFile(XmlToEmber& parser, string filename, vector>& embers, bool useDefaults = true) { - if (!parser.Parse(filename.c_str(), embers)) + if (!parser.Parse(filename.c_str(), embers, useDefaults)) { cout << "Error parsing flame file " << filename << ", returning without executing." << endl; return false; diff --git a/Source/EmberGenome/EmberGenome.cpp b/Source/EmberGenome/EmberGenome.cpp index 4292f82..b4b7f18 100644 --- a/Source/EmberGenome/EmberGenome.cpp +++ b/Source/EmberGenome/EmberGenome.cpp @@ -239,7 +239,7 @@ bool EmberGenome(EmberOptions& opt) if (opt.TemplateFile() != "") { - if (!ParseEmberFile(parser, opt.TemplateFile(), templateEmbers)) + if (!ParseEmberFile(parser, opt.TemplateFile(), templateEmbers, false))//Do not use defaults here to ensure only present fields get used when applying the template. return false; if (templateEmbers.size() > 1) From fb262c2469ef13d1dc3fe243e48d0bb9baf8df62 Mon Sep 17 00:00:00 2001 From: mfeemster Date: Mon, 6 Jul 2015 21:36:46 -0700 Subject: [PATCH 09/10] --User changes -Add motion support from Simon Detheridge at the ember level instead of just individual xforms: Add the ability to manipulate camera pitch, yaw and other goodies during flame rotation using a new element specified at the top-level of the flame. Create a new 'saw' motion function, so that these values can effectively be looped (e.g. yaw -1 to +1) over the course of a rotation. Add an offset to existing motion elements, to start them partway through their cycle. This would (for example) enable creating circular motion of xform affines, by combining two offset sine waves, one with an offset of 0.25 or 0.75. Fix loops in EmberGenome (they only had an on/off effect - this was broken in flame-genome as well) and make the loop count floating-point as well. For sequence animations, it's not necessary for clips to loop precisely if they're not designed specifically for the ES project. Similarly, there's no need for motion_frequency to be an integer value either so this was changed to allow motion that doesn't necessarily start or end at the loop boundary. I've attempted to keep each bit of functionality in its own commit. There's an argument as to whether to call the new flame motion elements (to differentiate programmatically) or just (for consistency within the file) -- I opted for the former because it was easier to modify the xml parser that way. --Code changes -Change FlameMotion.h to EmberMotion.h to keep the naming convention consistent. -Made elements of EmberMotion.m_MotionParams into their own type, MotionParam, which allows for CopyVec() to work. -Change m_FlameMotionElements to m_EmberMotionElements in Ember. -Use CopyVec() for EmberMotion instead of manual copy in copy constructors. -Add exports in Ember.cpp for EmberMotion. -Format eEmberMotionParam enum with one entry per line since it has many entries. -Use fabs() in XmlToEmber instead of glm::abs. -Minor formatting. --- Builds/MSVC/VS2013/Ember.vcxproj | 1 + Builds/MSVC/VS2013/Ember.vcxproj.filters | 3 ++ Builds/QtCreator/Ember/Ember.pro | 2 +- Source/Ember/Ember.cpp | 3 ++ Source/Ember/Ember.h | 18 +++---- Source/Ember/EmberDefines.h | 26 +++++++-- Source/Ember/EmberToXml.h | 15 +++--- Source/Ember/FlameMotion.h | 66 ----------------------- Source/Ember/Point.h | 4 ++ Source/Ember/SheepTools.h | 4 +- Source/Ember/XmlToEmber.h | 67 +++++++++++++----------- Source/EmberCommon/EmberOptions.h | 2 +- Source/EmberGenome/EmberGenome.cpp | 6 +-- 13 files changed, 90 insertions(+), 127 deletions(-) delete mode 100644 Source/Ember/FlameMotion.h diff --git a/Builds/MSVC/VS2013/Ember.vcxproj b/Builds/MSVC/VS2013/Ember.vcxproj index 4016a78..25d86c3 100644 --- a/Builds/MSVC/VS2013/Ember.vcxproj +++ b/Builds/MSVC/VS2013/Ember.vcxproj @@ -271,6 +271,7 @@ + diff --git a/Builds/MSVC/VS2013/Ember.vcxproj.filters b/Builds/MSVC/VS2013/Ember.vcxproj.filters index 59e3bf5..ab751a1 100644 --- a/Builds/MSVC/VS2013/Ember.vcxproj.filters +++ b/Builds/MSVC/VS2013/Ember.vcxproj.filters @@ -113,6 +113,9 @@ Header Files + + Header Files + diff --git a/Builds/QtCreator/Ember/Ember.pro b/Builds/QtCreator/Ember/Ember.pro index b0e57e1..f4da780 100644 --- a/Builds/QtCreator/Ember/Ember.pro +++ b/Builds/QtCreator/Ember/Ember.pro @@ -53,5 +53,5 @@ HEADERS += \ ../../../Source/Ember/VariationsDC.h \ ../../../Source/Ember/Xform.h \ ../../../Source/Ember/XmlToEmber.h \ - ../../../Source/Ember/FlameMotion.h + ../../../Source/Ember/EmberMotion.h diff --git a/Source/Ember/Ember.cpp b/Source/Ember/Ember.cpp index 9684061..89009c6 100644 --- a/Source/Ember/Ember.cpp +++ b/Source/Ember/Ember.cpp @@ -24,6 +24,7 @@ template<> unique_ptr> QTIsaac; \ template EMBER_API class XaosIterator; \ template EMBER_API class Xform; \ + template EMBER_API class MotionParam; \ + template EMBER_API class EmberMotion; \ template EMBER_API class IteratorHelper; \ template EMBER_API class Variation; \ template EMBER_API class ParamWithName; \ diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index b02cd76..af394ae 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -5,7 +5,7 @@ #include "PaletteList.h" #include "SpatialFilter.h" #include "TemporalFilter.h" -#include "FlameMotion.h" +#include "EmberMotion.h" /// /// Ember class. @@ -184,10 +184,7 @@ public: if (ember.m_Edits != nullptr) m_Edits = xmlCopyDoc(ember.m_Edits, 1); - m_FlameMotionElements.clear(); - - for(int i = 0; i < ember.m_FlameMotionElements.size(); ++i) - m_FlameMotionElements.push_back(ember.m_FlameMotionElements[i]); + CopyVec(m_EmberMotionElements, ember.m_EmberMotionElements); return *this; } @@ -479,7 +476,7 @@ public: for (size_t i = 0; i < TotalXformCount(); i++) GetTotalXform(i)->DeleteMotionElements(); - m_FlameMotionElements.clear(); + m_EmberMotionElements.clear(); } /// @@ -1294,19 +1291,20 @@ public: } #define APP_FMP(x) x += param.second * Interpolater::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * (blend + motion.m_MotionOffset)) + /// /// Update ember parameters based on stored motion elements /// /// The time percentage value which dictates how much of a percentage of 360 degrees it should be rotated and the time position for the motion elements void ApplyFlameMotion(T blend) { - for (size_t i = 0; i < m_FlameMotionElements.size(); ++i) + for (size_t i = 0; i < m_EmberMotionElements.size(); ++i) { - const FlameMotion &motion = m_FlameMotionElements[i]; + auto& motion = m_EmberMotionElements[i]; for (size_t j = 0; j < motion.m_MotionParams.size(); ++j) { - const pair ¶m = motion.m_MotionParams[j]; + auto& param = motion.m_MotionParams[j]; switch (param.first) { @@ -1768,7 +1766,7 @@ public: size_t m_Index; //The list of motion elements for the top-level flame params - vector> m_FlameMotionElements; + vector> m_EmberMotionElements; private: /// diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index 92e69ae..43107eb 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -120,10 +120,26 @@ enum eProcessState : uint { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DO enum eInteractiveFilter : uint { FILTER_LOG = 0, FILTER_DE = 1 }; enum eScaleType : uint { SCALE_NONE = 0, SCALE_WIDTH = 1, SCALE_HEIGHT = 2 }; enum eRenderStatus : uint { RENDER_OK = 0, RENDER_ERROR = 1, RENDER_ABORT = 2 }; -enum eFlameMotionParam : uint { - FLAME_MOTION_NONE = 0, FLAME_MOTION_ZOOM = 1, FLAME_MOTION_ZPOS = 2, FLAME_MOTION_PERSPECTIVE = 3, FLAME_MOTION_YAW = 4, FLAME_MOTION_PITCH = 5, FLAME_MOTION_DEPTH_BLUR = 6, - FLAME_MOTION_CENTER_X = 7, FLAME_MOTION_CENTER_Y = 8, FLAME_MOTION_ROTATE = 9, FLAME_MOTION_HUE = 10, FLAME_MOTION_BRIGHTNESS = 11, FLAME_MOTION_GAMMA = 12, - FLAME_MOTION_GAMMA_THRESH = 13, FLAME_MOTION_HIGHLIGHT_POWER = 14, FLAME_MOTION_BACKGROUND_R = 15, FLAME_MOTION_BACKGROUND_G = 16, - FLAME_MOTION_BACKGROUND_B = 17, FLAME_MOTION_VIBRANCY = 18 +enum eEmberMotionParam : uint +{ + FLAME_MOTION_NONE = 0, + FLAME_MOTION_ZOOM = 1, + FLAME_MOTION_ZPOS = 2, + FLAME_MOTION_PERSPECTIVE = 3, + FLAME_MOTION_YAW = 4, + FLAME_MOTION_PITCH = 5, + FLAME_MOTION_DEPTH_BLUR = 6, + FLAME_MOTION_CENTER_X = 7, + FLAME_MOTION_CENTER_Y = 8, + FLAME_MOTION_ROTATE = 9, + FLAME_MOTION_HUE = 10, + FLAME_MOTION_BRIGHTNESS = 11, + FLAME_MOTION_GAMMA = 12, + FLAME_MOTION_GAMMA_THRESH = 13, + FLAME_MOTION_HIGHLIGHT_POWER = 14, + FLAME_MOTION_BACKGROUND_R = 15, + FLAME_MOTION_BACKGROUND_G = 16, + FLAME_MOTION_BACKGROUND_B = 17, + FLAME_MOTION_VIBRANCY = 18 }; } diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index f79ba8a..4080b74 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -221,8 +221,8 @@ public: os << "\">\n"; - for (i = 0; i < ember.m_FlameMotionElements.size(); ++i) - os << " " << ToString(ember.m_FlameMotionElements[i]); + 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) @@ -729,7 +729,7 @@ private: /// Convert a FlameMotion element to an xml string /// /// The FlameMotion object to convert to XML - string ToString(const FlameMotion &motion) + string ToString(const EmberMotion& motion) { ostringstream os; os << "\n"; diff --git a/Source/Ember/FlameMotion.h b/Source/Ember/FlameMotion.h deleted file mode 100644 index 7f47d90..0000000 --- a/Source/Ember/FlameMotion.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include "EmberDefines.h" - -namespace EmberNs -{ - -/// -/// FlameMotion elements allow for motion of the flame parameters such as zoom, yaw, pitch and friends -/// The values in these elements can be used to modify flame parameters during rotation in much the same -/// way as motion elements on xforms do. -/// Template argument expected to be float or double. -/// -template -class EMBER_API FlameMotion -{ -public: - - FlameMotion() - { - m_MotionFreq = 0; - m_MotionFunc = MOTION_SIN; - m_MotionOffset = 0; - } - - FlameMotion(const FlameMotion &other) - { - operator=(other); - } - - template - FlameMotion(const FlameMotion &other) - { - operator=(other); - } - - FlameMotion& operator = (const FlameMotion& other) - { - if (this != &other) - FlameMotion::operator=(other); - - return *this; - } - - template - FlameMotion &operator = (const FlameMotion &other) - { - m_MotionParams.clear(); - - for (int i = 0; i < other.m_MotionParams.size(); ++i) - m_MotionParams.push_back(pair(other.m_MotionParams[i].first, T(other.m_MotionParams[i].second))); - - m_MotionFunc = other.m_MotionFunc; - m_MotionFreq = T(other.m_MotionFreq); - m_MotionOffset = T(other.m_MotionOffset); - return *this; - } - - T m_MotionFreq; - T m_MotionOffset; - eMotion m_MotionFunc; - vector> m_MotionParams; - -}; - -} diff --git a/Source/Ember/Point.h b/Source/Ember/Point.h index b157b6d..a60cd62 100644 --- a/Source/Ember/Point.h +++ b/Source/Ember/Point.h @@ -196,6 +196,10 @@ public: /// /// Member-wise constructor. /// + /// The red value, either 0-1 or 0-255. + /// The green value, either 0-1 or 0-255. + /// The blue value, either 0-1 or 0-255. + /// The alpha value, either 0-1 or 0-255. Color(T rr, T gg, T bb, T aa) : v4T(rr, gg, bb, aa) { diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h index b01ac14..07c3d2f 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -1013,10 +1013,8 @@ public: } rotated.ApplyFlameMotion(blend); - rotated.RotateAffines(-blend * 360);//Rotate the affines. - - rotated.DeleteMotionElements(); // delete all motion elements from the looped flame + rotated.DeleteMotionElements();//Delete all motion elements from the looped ember, at the xform level and at the parent ember level. } /// diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 6c7fb44..9612876 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -978,7 +978,7 @@ private: if (theXform) { //Check for non-zero motion params. - if (abs(theXform->m_MotionFreq) > 0.0)//Original checked for motion func being non-zero, but it was set to MOTION_SIN (1) in Xform::Init(), so don't check for 0 here. + if (fabs(theXform->m_MotionFreq) > 0.0)//Original checked for motion func being non-zero, but it was set to MOTION_SIN (1) in Xform::Init(), so don't check for 0 here. { m_ErrorReport.push_back(string(loc) + " : Motion parameters should not be specified in regular, non-motion xforms"); } @@ -1007,7 +1007,7 @@ private: } else if (!Compare(childNode->name, "flame_motion")) { - FlameMotion motion; + EmberMotion motion; att = childNode->properties; @@ -1026,13 +1026,14 @@ private: else if (!Compare(curAtt->name, "motion_function")) { string func(attStr); - if ( func == "sin" ) + + if (func == "sin") motion.m_MotionFunc = MOTION_SIN; - else if ( func == "triangle" ) + else if (func == "triangle") motion.m_MotionFunc = MOTION_TRIANGLE; - else if ( func == "hill" ) + else if (func == "hill") motion.m_MotionFunc = MOTION_HILL; - else if ( func == "saw" ) + else if (func == "saw") motion.m_MotionFunc = MOTION_SAW; else { @@ -1041,31 +1042,31 @@ private: } } else if (!Compare(curAtt->name, "zoom")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ZOOM, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_ZOOM, motion); else if (!Compare(curAtt->name, "cam_zpos")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ZPOS, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_ZPOS, motion); else if (!Compare(curAtt->name, "cam_persp")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_PERSPECTIVE, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_PERSPECTIVE, motion); else if (!Compare(curAtt->name, "cam_yaw")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_YAW, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_YAW, motion); else if (!Compare(curAtt->name, "cam_pitch")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_PITCH, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_PITCH, motion); else if (!Compare(curAtt->name, "cam_dof")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_DEPTH_BLUR, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_DEPTH_BLUR, motion); else if (!Compare(curAtt->name, "rotate")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ROTATE, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_ROTATE, motion); else if (!Compare(curAtt->name, "hue")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_HUE, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_HUE, motion); else if (!Compare(curAtt->name, "brightness")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_BRIGHTNESS, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_BRIGHTNESS, motion); else if (!Compare(curAtt->name, "gamma")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_GAMMA, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_GAMMA, motion); else if (!Compare(curAtt->name, "gamma_threshold")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_GAMMA_THRESH, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_GAMMA_THRESH, motion); else if (!Compare(curAtt->name, "highlight_power")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_HIGHLIGHT_POWER, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_HIGHLIGHT_POWER, motion); else if (!Compare(curAtt->name, "vibrancy")) - ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_VIBRANCY, motion); + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_VIBRANCY, motion); else if (!Compare(curAtt->name, "background")) { double r, g, b; @@ -1078,11 +1079,13 @@ private: } if (r != 0) - motion.m_MotionParams.push_back(pair(FLAME_MOTION_BACKGROUND_R, T(r))); + motion.m_MotionParams.push_back(MotionParam(FLAME_MOTION_BACKGROUND_R, T(r))); + if (g != 0) - motion.m_MotionParams.push_back(pair(FLAME_MOTION_BACKGROUND_G, T(g))); + motion.m_MotionParams.push_back(MotionParam(FLAME_MOTION_BACKGROUND_G, T(g))); + if (b != 0) - motion.m_MotionParams.push_back(pair(FLAME_MOTION_BACKGROUND_B, T(b))); + motion.m_MotionParams.push_back(MotionParam(FLAME_MOTION_BACKGROUND_B, T(b))); } else if (!Compare(curAtt->name, "center")) { @@ -1096,9 +1099,10 @@ private: } if (cx != 0) - motion.m_MotionParams.push_back(pair(FLAME_MOTION_CENTER_X, T(cx))); + motion.m_MotionParams.push_back(MotionParam(FLAME_MOTION_CENTER_X, T(cx))); + if (cy != 0) - motion.m_MotionParams.push_back(pair(FLAME_MOTION_CENTER_Y, T(cy))); + motion.m_MotionParams.push_back(MotionParam(FLAME_MOTION_CENTER_Y, T(cy))); } else { @@ -1110,7 +1114,7 @@ private: xmlFree(attStr); } - currentEmber.m_FlameMotionElements.push_back(motion); + currentEmber.m_EmberMotionElements.push_back(motion); } } @@ -1125,26 +1129,27 @@ private: } /// - /// Parse a floating point value from an xml attribute and add the value to a FlameMotion object + /// Parse a floating point value from an xml attribute and add the value to a EmberMotion object /// /// The current attribute /// The attribute value to parse /// The flame motion parameter type /// The flame motion element to add the parameter to /// True if there were no errors, else false. - bool AttToFlameMotionFloat(xmlAttrPtr att, const char *attStr, eFlameMotionParam param, FlameMotion &motion) + bool AttToEmberMotionFloat(xmlAttrPtr att, const char* attStr, eEmberMotionParam param, EmberMotion& motion) { const char* loc = __FUNCTION__; - bool r = false; T val = 0.0; if (Atof(attStr, val)) { - motion.m_MotionParams.push_back(pair(param, val)); + motion.m_MotionParams.push_back(MotionParam(param, val)); r = true; - } else { - m_ErrorReport.push_back(string(loc) + " : Failed to parse float value for flame motion attribute \"" + string(CCX(att->name)) + "\" : " + string(attStr) + ""); + } + else + { + m_ErrorReport.push_back(string(loc) + " : Failed to parse float value for flame motion attribute \"" + string(CCX(att->name)) + "\" : " + string(attStr)); } return r; diff --git a/Source/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h index fc7e453..87e2790 100644 --- a/Source/EmberCommon/EmberOptions.h +++ b/Source/EmberCommon/EmberOptions.h @@ -375,7 +375,7 @@ public: INITDOUBLEOPTION(OffsetX, Eod(OPT_USE_GENOME, OPT_OFFSETX, _T("--offsetx"), 0.0, SO_REQ_SEP, "\t--offsetx= Amount to jitter each flame horizontally when applying genome tools [default: 0].\n")); INITDOUBLEOPTION(OffsetY, Eod(OPT_USE_GENOME, OPT_OFFSETY, _T("--offsety"), 0.0, SO_REQ_SEP, "\t--offsety= Amount to jitter each flame vertically when applying genome tools [default: 0].\n")); INITDOUBLEOPTION(UseMem, Eod(OPT_USE_RENDER, OPT_USEMEM, _T("--use_mem"), 0.0, SO_REQ_SEP, "\t--use_mem= Number of bytes of memory to use [default: max system memory].\n")); - INITDOUBLEOPTION(Loops, Eod(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops= Number of times to rotate each control point in sequence [default: 1].\n")); + INITDOUBLEOPTION(Loops, Eod(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1.0, SO_REQ_SEP, "\t--loops= Number of times to rotate each control point in sequence [default: 1].\n")); //String. INITSTRINGOPTION(IsaacSeed, Eos(OPT_USE_ALL, OPT_ISAAC_SEED, _T("--isaac_seed"), "", SO_REQ_SEP, "\t--isaac_seed= Character-based seed for the random number generator [default: random].\n")); diff --git a/Source/EmberGenome/EmberGenome.cpp b/Source/EmberGenome/EmberGenome.cpp index e8c3569..d62bc17 100644 --- a/Source/EmberGenome/EmberGenome.cpp +++ b/Source/EmberGenome/EmberGenome.cpp @@ -395,14 +395,14 @@ bool EmberGenome(EmberOptions& opt) if (i < embers.size() - 1) { vector> interpEmbers; + interpEmbers.push_back(embers[i]); interpEmbers.push_back(embers[i + 1]); if (opt.Loops() > 0) { - // we might have looped a non-integral number of times, so store the last result as our flame to interpolate from - - interpEmbers[i] = result; + //We might have looped a non-integral number of times, so store the last result as our flame to interpolate from. + interpEmbers[i] = result; } for (frame = 0; frame < opt.Frames(); frame++) From 3aa9e1319435c2dbe1c0a1fdb2f803a7b8d2a4dc Mon Sep 17 00:00:00 2001 From: mfeemster Date: Wed, 15 Jul 2015 20:27:32 -0700 Subject: [PATCH 10/10] --User changes -Add Summary tab to the info dock, place existing bounds related info in a new Bounds tab. -Add a toolbar with the most common actions on it. -Remove old toolbar at the top of the library tab. --Code changes -Refactor code to make a color visible on a background into VisibleColor() in FractoriumCommon.h -Add IsXformLinked() to determine if an xform is linked to another. -Remove SetPaletteRefTable() and GetQRgbFromPaletteIndex() from FractoriumEmberControllerBase and its derivations, unused. -Add FillSummary() to FractoriumEmberController. Call in Update(), UpdateXform() and other places where values are changed to force an update of the summary view. -Add export statements to FractoriumInfo.cpp. -Remove image parameter from SetPaletteTableItem(), it was a bad design. -InitToolbarUI() is now empty since toolbar initialization happens automatically. Leave as a placeholder. -Refactor code to get a palette color at a given index and convert it to a QColor to ColorIndexToQColor(). --- Source/Fractorium/Fractorium.cpp | 8 +- Source/Fractorium/Fractorium.h | 19 +- Source/Fractorium/Fractorium.ui | 1813 ++++++++++------- Source/Fractorium/FractoriumCommon.h | 75 + .../Fractorium/FractoriumEmberController.cpp | 5 + Source/Fractorium/FractoriumEmberController.h | 10 +- Source/Fractorium/FractoriumInfo.cpp | 219 ++ Source/Fractorium/FractoriumLibrary.cpp | 3 +- Source/Fractorium/FractoriumPalette.cpp | 2 +- Source/Fractorium/FractoriumParams.cpp | 8 +- Source/Fractorium/FractoriumToolbar.cpp | 13 +- Source/Fractorium/FractoriumXformsAffine.cpp | 4 +- Source/Fractorium/FractoriumXformsColor.cpp | 53 +- .../Fractorium/FractoriumXformsVariations.cpp | 2 + Source/Fractorium/OptionsDialog.ui | 6 +- 15 files changed, 1455 insertions(+), 785 deletions(-) diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index b2d3b42..d47e530 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -103,6 +103,7 @@ Fractorium::Fractorium(QWidget* p) InitXaosUI(); InitPaletteUI(); InitLibraryUI(); + InitInfoUI(); InitMenusUI(); //This will init the controller and fill in the variations and palette tables with template specific instances @@ -175,6 +176,7 @@ Fractorium::Fractorium(QWidget* p) ui.XformPaletteRefTable->setStyleSheet("QTableWidget::item { padding: 0px; border: none; margin: 0px; }"); ui.PaletteAdjustTable->setStyleSheet("QTableWidget::item { padding: 1px; }");//Need this to avoid covering the top border pixel with the spinners. ui.statusBar->setStyleSheet("QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }"); + ui.XaosTableView->setStyleSheet("QTableView { margin: 1px}"); //setStyleSheet("QGroupBox { border: 2px solid gray; border-radius: 3px; } "); @@ -747,7 +749,11 @@ void Fractorium::SetTabOrders() w = SetTabOrder(this, w, ui.PaletteFilterClearButton); w = SetTabOrder(this, w, ui.PaletteListTable); - w = SetTabOrder(this, ui.InfoBoundsGroupBox, ui.InfoBoundsFrame);//Info. + + w = SetTabOrder(this, ui.SummaryTableWidget, ui.SummaryTreeWidget);//Info summary. + + w = SetTabOrder(this, ui.InfoBoundsGroupBox, ui.InfoBoundsFrame);//Info bounds. + w = SetTabOrder(this, w, ui.InfoBoundsTable); w = SetTabOrder(this, w, ui.InfoFileOpeningGroupBox); w = SetTabOrder(this, w, ui.InfoFileOpeningTextEdit); diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index ac92fe1..9f5fc9f 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -142,9 +142,6 @@ public slots: void OnActionAbout(bool checked);//Help. //Toolbar. - void OnSaveCurrentAsXmlButtonClicked(bool checked); - void OnSaveEntireFileAsXmlButtonClicked(bool checked); - void OnSaveCurrentToOpenedFileButtonClicked(bool checked); //Library. void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col); @@ -275,6 +272,10 @@ public slots: void OnPaletteFilterClearButtonClicked(bool checked); void OnPaletteHeaderSectionClicked(int col); + //Info. + void OnSummaryTableHeaderResized(int logicalIndex, int oldSize, int newSize); + void OnSummaryTreeHeaderSectionClicked(int logicalIndex); + //Rendering/progress. void StartRenderTimer(); void IdleTimer(); @@ -311,6 +312,7 @@ private: void InitXaosUI(); void InitPaletteUI(); void InitLibraryUI(); + void InitInfoUI(); void SetTabOrders(); void ToggleTableRow(QTableView* table, int logicalIndex); @@ -342,10 +344,13 @@ private: //Palette. void ResetPaletteControls(); void SetPaletteFileComboIndex(const string& filename); + void SetPaletteTableItem(QPixmap* pixmap, QTableWidget* table, QTableWidgetItem* item, int row, int col); //Info. + void FillSummary(); void UpdateHistogramBounds(); void ErrorReportToQTextEdit(const vector& errors, QTextEdit* textEdit, bool clear = true); + void SetTableWidgetBackgroundColor(); //Rendering/progress. bool CreateRendererFromOptions(); @@ -441,6 +446,14 @@ private: SpinBox* m_PaletteBlurSpin; SpinBox* m_PaletteFrequencySpin; + //Info. + QTableWidgetItem* m_InfoNameItem; + QTableWidgetItem* m_InfoPaletteItem; + QTableWidgetItem* m_Info3dItem; + QTableWidgetItem* m_InfoXaosItem; + QTableWidgetItem* m_InfoXformCountItem; + QTableWidgetItem* m_InfoFinalXformItem; + //Files. QFileDialog* m_FileDialog; QFileDialog* m_FolderDialog; diff --git a/Source/Fractorium/Fractorium.ui b/Source/Fractorium/Fractorium.ui index caf978c..d2af808 100644 --- a/Source/Fractorium/Fractorium.ui +++ b/Source/Fractorium/Fractorium.ui @@ -6,7 +6,7 @@ 0 0 - 1418 + 1442 926 @@ -74,8 +74,8 @@ 0 0 - 1217 - 885 + 1175 + 861 @@ -1551,19 +1551,31 @@ 4 - 5 + 4 - 5 + 4 - 5 + 4 4 + + 4 + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + @@ -1572,6 +1584,15 @@ Clear Xaos + + false + + + false + + + false + @@ -1715,7 +1736,7 @@ 240 - 200 + 498 @@ -1741,202 +1762,6 @@ 4 - - - - - - - - 2 - 0 - - - - - 0 - 67 - - - - - 16777215 - 67 - - - - Qt::NoFocus - - - QFrame::Panel - - - QFrame::Plain - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - false - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::NoSelection - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - false - - - 3 - - - 4 - - - false - - - 62 - - - 62 - - - true - - - false - - - 22 - - - false - - - 22 - - - - - - - - - - - Hue - - - - - Contrast - - - - - Saturation - - - - - Blur - - - - - Brightness - - - - - - - - - - Frequency - - - - - - - - 4 - - - 0 - - - 0 - - - - - - 0 - 24 - - - - - 16777215 - 24 - - - - Select a random palette from the list - - - Random Palette - - - - - - - - 0 - 24 - - - - - 16777215 - 24 - - - - Apply a random adjustment to the current palette - - - Random Adjustment - - - false - - - - - @@ -2140,6 +1965,202 @@ + + + + + + + + 2 + 0 + + + + + 0 + 67 + + + + + 16777215 + 67 + + + + Qt::NoFocus + + + QFrame::Panel + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + false + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + 3 + + + 4 + + + false + + + 62 + + + 62 + + + true + + + false + + + 22 + + + false + + + 22 + + + + + + + + + + + Hue + + + + + Contrast + + + + + Saturation + + + + + Blur + + + + + Brightness + + + + + + + + + + Frequency + + + + + + + + 4 + + + 0 + + + 0 + + + + + + 0 + 24 + + + + + 16777215 + 24 + + + + Select a random palette from the list + + + Random Palette + + + + + + + + 0 + 24 + + + + + 16777215 + 24 + + + + Apply a random adjustment to the current palette + + + Random Adjustment + + + false + + + + + @@ -3284,7 +3305,7 @@ SpinBox 0 0 - 237 + 243 745 @@ -4941,7 +4962,7 @@ SpinBox 0 0 - 237 + 243 680 @@ -4998,15 +5019,15 @@ SpinBox - 1030 + 1000 0 - 200 + 301 881 - 80 + 137 200 @@ -5019,7 +5040,7 @@ SpinBox Info - + 6 @@ -5037,7 +5058,13 @@ SpinBox 4 - + + + + 0 + 0 + + QFrame::NoFrame @@ -5047,18 +5074,27 @@ SpinBox 1 + + Qt::ScrollBarAsNeeded + true - + 0 0 - 190 + 291 851 + + + 0 + 0 + + 0 @@ -5076,451 +5112,820 @@ SpinBox 0 - + - + 0 0 - - - 0 - 250 - + + QTabWidget::Triangular - - - 16777215 - 250 - + + 0 - - - 0 - 0 - - - - Qt::StrongFocus - - - false - - - Histogram Bounds - - - - 4 - - - 6 - - - 4 - - - 6 - - - 6 - - - - - - 0 - 0 - - - - - 0 - 173 - - - - - 16777215 - 173 - - - - false - - - QFrame::Box - - - QFrame::Plain - - - - 2 + + + Summary + + + + 5 + + + 5 + + + 4 + + + 5 + + + 4 + + + + + + 0 + 0 + - - 2 + + + 0 + 128 + - - 2 + + + 16777215 + 128 + - - 2 + + Qt::NoFocus - - 2 + + QFrame::StyledPanel + + QFrame::Plain + + + 1 + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerItem + + + true + + + false + + + false + + + false + + + 22 + + + 22 + + + true + + + true + + + false + + + 21 + + + 21 + + + false + + + + Name + + + + + Palette + + + + + 3D Used + + + + + Xaos Used + + + + + Xform Count + + + + + Final Xform + + + + + + + - - - QFrame::Box - - - QFrame::Plain - - - UL: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - + + Test Flame + - - - - QFrame::Box - - - LR: - - - Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing - - - - - - - QFrame::Box - - - UR: - - - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing - - - - - - - QFrame::Box - - - W x H: - - - Qt::AlignCenter - - + + + + - - - QFrame::Box + + Yes + + + + + Yes + + + + + 5 + + + + + Yes + + + + + + + + + 0 + 0 + + + + QFrame::Plain + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAsNeeded + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoSelection + + + true + + + true + + + 2 + + + true + + + false + + + 100 + + + 30 + + + false + + + true + + + + + + + + + + + + + + Xform 1 + + + Xform 1 Name + + + + Pre Affine - LL: + 1, 2, 3, 4, 5, 6 - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + Post Affine - + + 7, 8, 9, 10, 11, 12 + + + + + Variation 1 + + + 1.234 + + + + Var1p1 + + + 2.4456 + + + + + Var1p2 + + + 3.56 + + + + + + Variation 2 + + + 2.2 + + - - - - - - - - 0 - 0 - - - - - 0 - 46 - - - - - 16777215 - 46 - - - - - true - - - - Qt::NoFocus - - - - - - QFrame::Panel - - - QFrame::Plain - - - 1 - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - false - - - QAbstractItemView::NoEditTriggers - - - false - - - false - - - false - - - QAbstractItemView::NoSelection - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - true - - - Qt::SolidLine - - - false - - - false - - - 2 - - - false - - - false - - - 110 - - - false - - - 27 - - - true - - - false - - - 22 - - - false - - - 22 - - - false - - - - Gutter - - - - - DE Box Dimensions - - - - - Field - - - - - - - - - - Gutter - - - - - 0 - - - - - DE Box Dimensions - - - - - 0 - - - - true - - - - AlignLeft|AlignVCenter - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Qt::StrongFocus - - - File Opening - - - - 4 + + + + + + + + + + + Xform 2 + + + + + + + + + + + + + + + + Final Xform + + + + + + + + + + + + + 0 + 0 + - - 6 - - - 4 - - - 6 - - - 6 - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - true - - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - Rendering - - - - 4 - - - 6 - - - 4 - - - 6 - - - 6 - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - true - - - true - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - + + Bounds + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 250 + + + + + 16777215 + 250 + + + + + 0 + 0 + + + + Qt::StrongFocus + + + false + + + Histogram Bounds + + + + 4 + + + 6 + + + 4 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 0 + 173 + + + + + 16777215 + 173 + + + + false + + + QFrame::Box + + + QFrame::Plain + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + QFrame::Box + + + QFrame::Plain + + + UL: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + QFrame::Box + + + LR: + + + Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing + + + + + + + QFrame::Box + + + UR: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + QFrame::Box + + + W x H: + + + Qt::AlignCenter + + + + + + + QFrame::Box + + + LL: + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + + + + 0 + 0 + + + + + 0 + 46 + + + + + 16777215 + 46 + + + + + true + + + + Qt::NoFocus + + + + + + QFrame::Panel + + + QFrame::Plain + + + 1 + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + false + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + Qt::SolidLine + + + false + + + false + + + 2 + + + false + + + false + + + 110 + + + false + + + 27 + + + true + + + false + + + 22 + + + false + + + 22 + + + false + + + + Gutter + + + + + DE Box Dimensions + + + + + Field + + + + + + + + + + Gutter + + + + + 0 + + + + + DE Box Dimensions + + + + + 0 + + + + true + + + + AlignLeft|AlignVCenter + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::StrongFocus + + + File Opening + + + + 4 + + + 6 + + + 4 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + true + + + + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + Rendering + + + + 4 + + + 6 + + + 4 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + true + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + @@ -5537,7 +5942,7 @@ SpinBox 0 0 - 1418 + 1442 21 @@ -5671,102 +6076,17 @@ SpinBox 4 - 6 + 5 - 3 + 5 - 6 + 5 - 6 + 4 - - - - 4 - - - - - - 0 - 24 - - - - - 16777215 - 24 - - - - Save the current flame as an xml file - - - - - - - :/Fractorium/Icons/database-medium.png:/Fractorium/Icons/database-medium.png - - - - - - - - 0 - 24 - - - - - 16777215 - 24 - - - - Save all flames as a single xml file - - - - - - - :/Fractorium/Icons/databases.png:/Fractorium/Icons/databases.png - - - - - - - - 0 - 24 - - - - - 16777215 - 24 - - - - <html><head/><body><p>Save the currently displayed flame back to the opened file in memory.</p><p>This overwrites the original flame but does not store the file back to disk.</p></body></html> - - - - - - - :/Fractorium/Icons/document-hf-insert.png:/Fractorium/Icons/document-hf-insert.png - - - - - @@ -5786,8 +6106,8 @@ SpinBox 0 0 - 188 - 824 + 256 + 830 @@ -5843,6 +6163,42 @@ SpinBox + + + toolBar + + + true + + + true + + + TopToolBarArea + + + false + + + + + + + + + + + + + + + + + + + + + @@ -6194,9 +6550,6 @@ SpinBox LibraryDockWidget - SaveCurrentAsXmlButton - SaveEntireFileAsXmlButton - SaveCurrentToOpenedFileButton diff --git a/Source/Fractorium/FractoriumCommon.h b/Source/Fractorium/FractoriumCommon.h index ff8f80d..0e318ce 100644 --- a/Source/Fractorium/FractoriumCommon.h +++ b/Source/Fractorium/FractoriumCommon.h @@ -111,3 +111,78 @@ static bool Exists(const QString& s) { return s != "" && QDir(s).exists(); } + +/// +/// Convert a color to one that is displayable on any background. +/// +/// The color to convert +/// The converted color +static QColor VisibleColor(const QColor& color) +{ + int threshold = 105; + int delta = (color.red() * 0.299) + //Magic numbers gotten from a Stack Overflow post. + (color.green() * 0.587) + + (color.blue() * 0.114); + + QColor textColor = (255 - delta < threshold) ? QColor(0, 0, 0) : QColor(255, 255, 255); + return textColor; +} + +/// +/// Determine whether an xform in an ember is linked to any other xform +/// in the ember. +/// +/// The ember which contains the xform +/// The xform to inspect +/// The index of the xform that the xform argument is linked to, else -1 +template +static intmax_t IsXformLinked(Ember& ember, Xform* xform) +{ + auto count = ember.XformCount(); + auto index = ember.GetXformIndex(xform); + intmax_t linked = -1; + size_t toOneCount = 0; + size_t toZeroCount = 0; + size_t toOneIndex = 0; + size_t fromOneCount = 0; + size_t fromZeroCount = 0; + size_t fromOneIndex = 0; + + if (index >= 0) + { + for (auto i = 0; i < count; i++) + { + if (xform->Xaos(i) == 0) + toZeroCount++; + else if (xform->Xaos(i) == 1) + { + toOneIndex = i; + toOneCount++; + } + } + + if ((toZeroCount == (count - 1)) && toOneCount == 1) + { + for (auto i = 0; i < count; i++) + { + if (auto fromXform = ember.GetXform(i)) + { + if (fromXform->Xaos(toOneIndex) == 0) + fromZeroCount++; + else if (fromXform->Xaos(toOneIndex) == 1) + { + fromOneIndex = i; + fromOneCount++; + } + } + } + + if ((fromZeroCount == (count - 1)) && fromOneCount == 1) + { + linked = toOneIndex; + } + } + } + + return linked; +} \ No newline at end of file diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp index 70a4dbe..76774bb 100644 --- a/Source/Fractorium/FractoriumEmberController.cpp +++ b/Source/Fractorium/FractoriumEmberController.cpp @@ -217,6 +217,7 @@ template void FractoriumEmberController::Update(std::function func, bool updateRender, eProcessAction action) { func(); + FillSummary(); if (updateRender) UpdateRender(action); @@ -294,6 +295,8 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu break; } + FillSummary(); + if (updateRender) UpdateRender(action); } @@ -305,6 +308,7 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu /// Resets the rendering process. /// /// The ember to set as the current +/// If true, do not overwrite temporal samples, quality or supersample value, else overwrite. template template void FractoriumEmberController::SetEmberPrivate(const Ember& ember, bool verbatim) @@ -335,6 +339,7 @@ void FractoriumEmberController::SetEmberPrivate(const Ember& ember, bool v m_GLController->ResetMouseState(); FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo. FillParamTablesAndPalette(); + FillSummary(); //If a resize happened, this won't do anything because the new size is not reflected in the scroll area yet. //However, it will have been taken care of in SyncSizes() in that case, so it's ok. diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index 4a6b5af..07f7207 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -178,7 +178,7 @@ public: virtual void XformColorSpeedChanged(double d) { } virtual void XformOpacityChanged(double d) { } virtual void XformDirectColorChanged(double d) { } - void SetPaletteRefTable(QPixmap* pixmap); + virtual QColor ColorIndexToQColor(double d) { return QColor(); } //Xforms Variations. virtual void SetupVariationTree() { } @@ -199,11 +199,12 @@ public: virtual bool FillPaletteTable(const string& s) { return false; } virtual void ApplyPaletteToEmber() { } virtual void PaletteAdjust() { } - virtual QRgb GetQRgbFromPaletteIndex(uint i) { return QRgb(); } virtual void PaletteCellClicked(int row, int col) { } + QImage& FinalPaletteImage() { return m_FinalPaletteImage; } //Info. - + virtual void FillSummary() { } + //Rendering/progress. virtual bool Render() { return false; } virtual bool CreateRenderer(eRendererType renderType, uint platform, uint device, bool shared = true) { return false; } @@ -413,6 +414,7 @@ public: virtual void XformColorSpeedChanged(double d) override; virtual void XformOpacityChanged(double d) override; virtual void XformDirectColorChanged(double d) override; + virtual QColor ColorIndexToQColor(double d) override; void FillColorWithXform(Xform* xform); //Xforms Variations. @@ -433,10 +435,10 @@ public: virtual bool FillPaletteTable(const string& s) override; virtual void ApplyPaletteToEmber() override; virtual void PaletteAdjust() override; - virtual QRgb GetQRgbFromPaletteIndex(uint i) override { return QRgb(); } virtual void PaletteCellClicked(int row, int col) override; //Info. + virtual void FillSummary() override; //Rendering/progress. virtual bool Render() override; diff --git a/Source/Fractorium/FractoriumInfo.cpp b/Source/Fractorium/FractoriumInfo.cpp index c0d014a..6066c02 100644 --- a/Source/Fractorium/FractoriumInfo.cpp +++ b/Source/Fractorium/FractoriumInfo.cpp @@ -1,6 +1,219 @@ #include "FractoriumPch.h" #include "Fractorium.h" +/// +/// Initialize the info UI. +/// +void Fractorium::InitInfoUI() +{ + auto treeHeader = ui.SummaryTreeWidget->header(); + auto tableHeader = ui.SummaryTableWidget->horizontalHeader(); + + treeHeader->setVisible(true); + treeHeader->setSectionsClickable(true); + treeHeader->setSectionResizeMode(QHeaderView::ResizeToContents); + connect(treeHeader, SIGNAL(sectionClicked(int)), this, SLOT(OnSummaryTreeHeaderSectionClicked(int)), Qt::QueuedConnection); + connect(tableHeader, SIGNAL(sectionResized(int, int, int)), this, SLOT(OnSummaryTableHeaderResized(int, int, int)), Qt::QueuedConnection); + SetFixedTableHeader(ui.SummaryTableWidget->verticalHeader()); + + ui.SummaryTableWidget->setItem(0, 0, m_InfoNameItem = new QTableWidgetItem("")); + ui.SummaryTableWidget->setItem(1, 0, m_InfoPaletteItem = new QTableWidgetItem("")); + ui.SummaryTableWidget->setItem(2, 0, m_Info3dItem = new QTableWidgetItem("")); + ui.SummaryTableWidget->setItem(3, 0, m_InfoXaosItem = new QTableWidgetItem("")); + ui.SummaryTableWidget->setItem(4, 0, m_InfoXformCountItem = new QTableWidgetItem("")); + ui.SummaryTableWidget->setItem(5, 0, m_InfoFinalXformItem = new QTableWidgetItem("")); +} + +/// +/// Called when the palette cell of the summary table is resized in response +/// to a resizing of the Info dock. +/// +/// Ignored +/// Ignored +/// Ignored +void Fractorium::OnSummaryTableHeaderResized(int logicalIndex, int oldSize, int newSize) +{ + QPixmap pixmap = QPixmap::fromImage(m_Controller->FinalPaletteImage());//Create a QPixmap out of the QImage. + SetPaletteTableItem(&pixmap, ui.SummaryTableWidget, m_InfoPaletteItem, 1, 0); +} + +/// +/// Expand or collapse the summary tree depending on the column index clicked. +/// 0: collapse, 1: expand. +/// +/// The column which was clicked +void Fractorium::OnSummaryTreeHeaderSectionClicked(int logicalIndex) +{ + auto tree = ui.SummaryTreeWidget; + + if (logicalIndex) + tree->expandAll(); + else + tree->collapseAll(); +} + +/// +/// Fill the summary tree with values from the current ember. +/// This is meant to be a rough summary by containing only the most relevant +/// values from the ember. +/// It's also meant to be used in a fire-and-forget way. Once the tree is filled +/// individual nodes are never referenced again. +/// The entire tree is cleared and refilled for every field change. +/// This would seem inefficient, but it appears to update with no flicker. +/// If this ever presents a problem in the future, revisit with a more +/// intelligent design. +/// +template +void FractoriumEmberController::FillSummary() +{ + int p = 3; + int vp = 4; + int vlen = 7; + char pc = 'f'; + size_t x = 0, total = m_Ember.TotalXformCount(); + Xform* xform = nullptr; + QColor color; + auto table = m_Fractorium->ui.SummaryTableWidget; + auto tree = m_Fractorium->ui.SummaryTreeWidget; + QVariantList states; + QTreeWidgetItemIterator it(tree); + + while (*it) + { + if (!(*it)->parent())//Top level only. + states += (*it)->isExpanded(); + + ++it; + } + + tree->blockSignals(true); + tree->clear(); + m_Fractorium->m_InfoNameItem->setText(m_Ember.m_Name.c_str()); + m_Fractorium->m_Info3dItem->setText(m_Ember.ProjBits() ? "Yes" : "No"); + m_Fractorium->m_InfoXaosItem->setText(m_Ember.XaosPresent() ? "Yes" : "No"); + m_Fractorium->m_InfoXformCountItem->setText(QString::number(m_Ember.XformCount())); + m_Fractorium->m_InfoFinalXformItem->setText(m_Ember.UseFinalXform() ? "Yes" : "No"); + + QPixmap pixmap = QPixmap::fromImage(m_FinalPaletteImage);//Create a QPixmap out of the QImage. + QSize size(table->columnWidth(0), table->rowHeight(1) + 1); + m_Fractorium->m_InfoPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + + for (x = 0; x < total && (xform = m_Ember.GetTotalXform(x)); x++) + { + size_t i = 0; + QString as = "Pre"; + auto item1 = new QTreeWidgetItem(tree); + intmax_t linkedIndex = IsXformLinked(m_Ember, xform); + QString linked = (linkedIndex != -1) ? (" Linked to " + QString::number(linkedIndex + 1)) : ""; + auto index = m_Ember.GetXformIndex(xform); + m_Ember.CalcNormalizedWeights(m_NormalizedWeights); + + if (!m_Ember.IsFinalXform(xform) && index != -1) + { + item1->setText(0, "Xform " + + QString::number(x + 1) + + " (" + QLocale::system().toString(xform->m_Weight, pc, p) + ") (" + + QLocale::system().toString(double(m_NormalizedWeights[index]), pc, p) + ") " + + linked); + } + else + item1->setText(0, "Final xform"); + + item1->setText(1, xform->m_Name.c_str()); + + auto affineItem = new QTreeWidgetItem(item1); + affineItem->setText(0, "Affine"); + + if (xform->m_Affine.IsZero()) + as += " Empty"; + else if (xform->m_Affine.IsID()) + as += " ID"; + + if (xform->HasPost()) + { + as += ", Post"; + + if (xform->m_Post.IsZero()) + as += " Empty";//Don't need to check further for IsID() because post is not included if it's ID. + } + + affineItem->setText(1, as); + + auto colorIndexItem = new QTreeWidgetItem(item1); + colorIndexItem->setText(0, "Color index"); + colorIndexItem->setText(1, QLocale::system().toString(xform->m_ColorX, pc, p)); + color = ColorIndexToQColor(xform->m_ColorX); + color.setAlphaF(xform->m_Opacity); + colorIndexItem->setBackgroundColor(1, color); + colorIndexItem->setTextColor(1, VisibleColor(color)); + + auto colorSpeedItem = new QTreeWidgetItem(item1); + colorSpeedItem->setText(0, "Color speed"); + colorSpeedItem->setText(1, QLocale::system().toString(xform->m_ColorSpeed, pc, p)); + + auto opacityItem = new QTreeWidgetItem(item1); + opacityItem->setText(0, "Opacity"); + opacityItem->setText(1, QLocale::system().toString(xform->m_Opacity, pc, p)); + + auto dcItem = new QTreeWidgetItem(item1); + dcItem->setText(0, "Direct color"); + dcItem->setText(1, QLocale::system().toString(xform->m_DirectColor, pc, p)); + + while (auto var = xform->GetVariation(i++)) + { + auto vitem = new QTreeWidgetItem(item1); + + vitem->setText(0, QString::fromStdString(var->Name())); + vitem->setText(1, QLocale::system().toString(var->m_Weight, pc, vp).rightJustified(vlen, ' ')); + + if (auto parVar = dynamic_cast*>(var)) + { + auto params = parVar->Params(); + + for (auto j = 0; j < parVar->ParamCount(); j++) + { + if (!params[j].IsPrecalc()) + { + auto pitem = new QTreeWidgetItem(vitem); + + pitem->setText(0, params[j].Name().c_str()); + pitem->setText(1, QLocale::system().toString(params[j].ParamVal(), pc, vp).rightJustified(vlen, ' ')); + } + } + } + } + + auto item2 = new QTreeWidgetItem(tree);//Empty item in between xforms. + } + + QTreeWidgetItemIterator it2(tree); + + if (!states.isEmpty()) + { + while (*it2) + { + if (!(*it2)->parent())//Top level only. + { + if (!states.isEmpty()) + (*it2)->setExpanded(states.takeFirst().toBool()); + else + (*it2)->setExpanded(true);//Expand any remainder when going from lesser to greater number of xforms. + } + + ++it2; + } + } + else + tree->expandAll(); + + tree->blockSignals(false); +} + +void Fractorium::FillSummary() +{ + m_Controller->FillSummary(); +} + /// /// Update the histogram bounds display labels. /// This shows the user the actual bounds of what's @@ -54,3 +267,9 @@ void Fractorium::ErrorReportToQTextEdit(const vector& errors, QTextEdit* for (auto& error : errors) QMetaObject::invokeMethod(textEdit, "append", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(error) + "\n")); } + +template class FractoriumEmberController; + +#ifdef DO_DOUBLE +template class FractoriumEmberController; +#endif diff --git a/Source/Fractorium/FractoriumLibrary.cpp b/Source/Fractorium/FractoriumLibrary.cpp index d63e40b..b780518 100644 --- a/Source/Fractorium/FractoriumLibrary.cpp +++ b/Source/Fractorium/FractoriumLibrary.cpp @@ -226,8 +226,9 @@ void FractoriumEmberController::EmberTreeItemChanged(QTreeWidgetItem* item, i m_Ember.m_Name = newName; m_LastSaveCurrent = "";//Reset will force the dialog to show on the next save current since the user probably wants a different name. } - + tree->blockSignals(false); + FillSummary(); } else if (QTreeWidgetItem* parentItem = dynamic_cast(item)) { diff --git a/Source/Fractorium/FractoriumPalette.cpp b/Source/Fractorium/FractoriumPalette.cpp index 00502c8..7623f46 100644 --- a/Source/Fractorium/FractoriumPalette.cpp +++ b/Source/Fractorium/FractoriumPalette.cpp @@ -192,7 +192,7 @@ void FractoriumEmberController::UpdateAdjustedPaletteGUI(Palette& palette) memcpy(m_FinalPaletteImage.scanLine(0), v.data(), v.size() * sizeof(v[0]));//Memcpy the data in. QPixmap pixmap = QPixmap::fromImage(m_FinalPaletteImage);//Create a QPixmap out of the QImage. previewPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(QSize(pixmap.width(), palettePreviewTable->rowHeight(0) + 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));//Set the pixmap on the palette tab. - SetPaletteRefTable(&pixmap);//Set the palette ref table on the xforms | color tab. + m_Fractorium->SetPaletteTableItem(&pixmap, m_Fractorium->ui.XformPaletteRefTable, m_Fractorium->m_PaletteRefItem, 0, 0);//Set the palette ref table on the xforms | color tab. QTableWidgetItem* previewNameItem = palettePreviewTable->item(0, 0); previewNameItem->setText(paletteName);//Finally, set the name of the palette to be both the text and the tooltip. diff --git a/Source/Fractorium/FractoriumParams.cpp b/Source/Fractorium/FractoriumParams.cpp index f434f14..f24c856 100644 --- a/Source/Fractorium/FractoriumParams.cpp +++ b/Source/Fractorium/FractoriumParams.cpp @@ -183,13 +183,7 @@ void FractoriumEmberController::BackgroundChanged(const QColor& color) QString g = ToString(color.green()); QString b = ToString(color.blue()); - int threshold = 105; - int delta = (color.red() * 0.299) + //Magic numbers gotten from a Stack Overflow post. - (color.green() * 0.587) + - (color.blue() * 0.114); - - QColor textColor = (255 - delta < threshold) ? QColor(0, 0, 0) : QColor(255, 255, 255); - colorTable->item(itemRow, 1)->setTextColor(textColor); + colorTable->item(itemRow, 1)->setTextColor(VisibleColor(color)); colorTable->item(itemRow, 1)->setText("rgb(" + r + ", " + g + ", " + b + ")"); //Color is 0-255, normalize to 0-1. diff --git a/Source/Fractorium/FractoriumToolbar.cpp b/Source/Fractorium/FractoriumToolbar.cpp index ad03925..8c74396 100644 --- a/Source/Fractorium/FractoriumToolbar.cpp +++ b/Source/Fractorium/FractoriumToolbar.cpp @@ -6,16 +6,5 @@ /// void Fractorium::InitToolbarUI() { - //These aren't menus but duplicate menu functionality in a pseudo-toolbar. - connect(ui.SaveCurrentAsXmlButton, SIGNAL(clicked(bool)), this, SLOT(OnSaveCurrentAsXmlButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.SaveEntireFileAsXmlButton, SIGNAL(clicked(bool)), this, SLOT(OnSaveEntireFileAsXmlButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.SaveCurrentToOpenedFileButton, SIGNAL(clicked(bool)), this, SLOT(OnSaveCurrentToOpenedFileButtonClicked(bool)), Qt::QueuedConnection); + //Empty for the moment, all functionality is handled in the designer. } - -/// -/// Wrappers around calls to menu items. -/// - -void Fractorium::OnSaveCurrentAsXmlButtonClicked(bool checked) { OnActionSaveCurrentAsXml(checked); } -void Fractorium::OnSaveEntireFileAsXmlButtonClicked(bool checked) { OnActionSaveEntireFileAsXml(checked); } -void Fractorium::OnSaveCurrentToOpenedFileButtonClicked(bool checked) { OnActionSaveCurrentToOpenedFile(checked); } diff --git a/Source/Fractorium/FractoriumXformsAffine.cpp b/Source/Fractorium/FractoriumXformsAffine.cpp index fcb40e3..4ffe748 100644 --- a/Source/Fractorium/FractoriumXformsAffine.cpp +++ b/Source/Fractorium/FractoriumXformsAffine.cpp @@ -150,8 +150,8 @@ void Fractorium::InitXformsAffineUI() //Further, the size of the dock widget won't be properly adjusted until the xforms tab is shown. //So show it here and it will be switched back in Fractorium's constructor. - //ui.ParamsTabWidget->setCurrentIndex(2); - //ui.DockWidget->update(); + //ui.ParamsTabWidget->setCurrentIndex(2); + //ui.DockWidget->update(); #endif //Placing pointers to the spin boxes in arrays makes them easier to access in various places. diff --git a/Source/Fractorium/FractoriumXformsColor.cpp b/Source/Fractorium/FractoriumXformsColor.cpp index 6c7474a..95efeaa 100644 --- a/Source/Fractorium/FractoriumXformsColor.cpp +++ b/Source/Fractorium/FractoriumXformsColor.cpp @@ -142,7 +142,9 @@ void Fractorium::OnSoloXformCheckBoxStateChanged(int state) /// Ignored void Fractorium::OnXformRefPaletteResized(int logicalIndex, int oldSize, int newSize) { - m_Controller->SetPaletteRefTable(nullptr); + QPixmap pixmap = QPixmap::fromImage(m_Controller->FinalPaletteImage()); + + SetPaletteTableItem(&pixmap, ui.XformPaletteRefTable, m_PaletteRefItem, 0, 0); } /// @@ -194,6 +196,25 @@ void Fractorium::OnCurvesRedRadioButtonToggled(bool checked) { if (checked) ui void Fractorium::OnCurvesGreenRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::GREEN); } void Fractorium::OnCurvesBlueRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::BLUE); } +/// +/// Look up the passed in index in the current ember's palette +/// and return the QColor equivalent. +/// +/// The palette index to look up, 0-1. +/// The palette color at the given index as a QColor +template +QColor FractoriumEmberController::ColorIndexToQColor(double d) +{ + v4T entry = m_Ember.m_Palette[Clamp(d * COLORMAP_LENGTH_MINUS_1, 0, m_Ember.m_Palette.Size())]; + + entry.r *= 255; + entry.g *= 255; + entry.b *= 255; + + QRgb rgb = uint(entry.r) << 16 | uint(entry.g) << 8 | uint(entry.b); + return QColor::fromRgb(rgb); +} + /// /// Set the selected xforms color index to the passed in value. /// Set the color cell in the palette ref table. @@ -207,14 +228,7 @@ void FractoriumEmberController::SetCurrentXformColorIndex(double d, bool upda xform->m_ColorX = Clamp(d, 0, 1); //Grab the current color from the index and assign it to the first cell of the first table. - v4T entry = m_Ember.m_Palette[Clamp(d * COLORMAP_LENGTH_MINUS_1, 0, m_Ember.m_Palette.Size())]; - - entry.r *= 255; - entry.g *= 255; - entry.b *= 255; - - QRgb rgb = uint(entry.r) << 16 | uint(entry.g) << 8 | uint(entry.b); - m_Fractorium->ui.XformColorIndexTable->item(0, 0)->setBackgroundColor(QColor::fromRgb(rgb)); + m_Fractorium->ui.XformColorIndexTable->item(0, 0)->setBackgroundColor(ColorIndexToQColor(xform->m_ColorX)/*QColor::fromRgb(rgb)*/); }, eXformUpdate::UPDATE_SELECTED, updateRender); } @@ -257,22 +271,19 @@ void FractoriumEmberController::FillColorWithXform(Xform* xform) } /// -/// Set the palette reference table to the passed in pixmap +/// Set the cell at the row and column in the passed in table to the passed in pixmap. /// -/// The pixmap -void FractoriumEmberControllerBase::SetPaletteRefTable(QPixmap* pixmap) +/// The pixmap to assign +/// The table whose cell will be filled with the image +/// The QTableWidgetItem in the cell +/// The row of the cell +/// The column of the cell +void Fractorium::SetPaletteTableItem(QPixmap* pixmap, QTableWidget* table, QTableWidgetItem* item, int row, int col) { - QSize size(m_Fractorium->ui.XformPaletteRefTable->columnWidth(0), m_Fractorium->ui.XformPaletteRefTable->rowHeight(0) + 1); - if (pixmap) { - m_Fractorium->m_PaletteRefItem->setData(Qt::DecorationRole, pixmap->scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } - else if (!m_FinalPaletteImage.isNull()) - { - QPixmap pixTemp = QPixmap::fromImage(m_FinalPaletteImage); - - m_Fractorium->m_PaletteRefItem->setData(Qt::DecorationRole, pixTemp.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + QSize size(table->columnWidth(col), table->rowHeight(row) + 1); + item->setData(Qt::DecorationRole, pixmap->scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); } } diff --git a/Source/Fractorium/FractoriumXformsVariations.cpp b/Source/Fractorium/FractoriumXformsVariations.cpp index 85b7b09..02843e3 100644 --- a/Source/Fractorium/FractoriumXformsVariations.cpp +++ b/Source/Fractorium/FractoriumXformsVariations.cpp @@ -150,6 +150,7 @@ void FractoriumEmberController::VariationSpinBoxValueChanged(double d)//Would { if (xformParVar->SetParamVal(sender->ParamName().c_str(), d)) { + FillSummary(); UpdateRender(); } } @@ -203,6 +204,7 @@ void FractoriumEmberController::VariationSpinBoxValueChanged(double d)//Would } } + FillSummary(); UpdateRender(); } } diff --git a/Source/Fractorium/OptionsDialog.ui b/Source/Fractorium/OptionsDialog.ui index 9c1a92a..a11eae5 100644 --- a/Source/Fractorium/OptionsDialog.ui +++ b/Source/Fractorium/OptionsDialog.ui @@ -7,7 +7,7 @@ 0 0 300 - 347 + 368 @@ -19,13 +19,13 @@ 300 - 347 + 368 300 - 347 + 368