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 58cf2c0..f4da780 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/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 da0726e..af394ae 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 "EmberMotion.h" /// /// Ember class. @@ -183,6 +184,8 @@ public: if (ember.m_Edits != nullptr) m_Edits = xmlCopyDoc(ember.m_Edits, 1); + CopyVec(m_EmberMotionElements, ember.m_EmberMotionElements); + return *this; } @@ -472,6 +475,8 @@ public: { for (size_t i = 0; i < TotalXformCount(); i++) GetTotalXform(i)->DeleteMotionElements(); + + m_EmberMotionElements.clear(); } /// @@ -1285,6 +1290,83 @@ public: point.m_Z -= m_CamZPos; } +#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_EmberMotionElements.size(); ++i) + { + auto& motion = m_EmberMotionElements[i]; + + for (size_t j = 0; j < motion.m_MotionParams.size(); ++j) + { + auto& param = 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. /// @@ -1683,6 +1765,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_EmberMotionElements; + private: /// /// The type of scaling used when resizing. diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index 534f3a6..43107eb 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -114,10 +114,32 @@ 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 }; 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 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 9d6cbf1..4080b74 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -221,6 +221,9 @@ public: os << "\">\n"; + for (i = 0; i < ember.m_EmberMotionElements.size(); ++i) + os << " " << ToString(ember.m_EmberMotionElements[i]); + //This is a grey area, what to do about symmetry to avoid duplicating the symmetry xforms when reading back?//TODO//BUG. //if (ember.m_Symmetry) // os << " \n"; @@ -488,6 +491,11 @@ 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\" "; + + if (xform.m_MotionOffset != 0) + os << "motion_offset=\"" << xform.m_MotionOffset << "\" "; } else { @@ -717,6 +725,114 @@ private: return os.str(); } + /// + /// Convert a FlameMotion element to an xml string + /// + /// The FlameMotion object to convert to XML + string ToString(const EmberMotion& motion) + { + ostringstream os; + os << " 0) + os << "motion_offset=\"" << motion.m_MotionOffset << "\" "; + + os << "motion_func="; + + switch (motion.m_MotionFunc) + { + case MOTION_SIN: + os << "\"sin\""; + break; + case MOTION_HILL: + os << "\"hill\""; + break; + case MOTION_TRIANGLE: + os << "\"triangle\""; + break; + case MOTION_SAW: + os << "\"saw\""; + break; + } + + T r = 0.0; + T g = 0.0; + T b = 0.0; + T cx = 0.0; + T cy = 0.0; + + for (int i = 0; i < motion.m_MotionParams.size(); ++i) + { + switch(motion.m_MotionParams[i].first) + { + case FLAME_MOTION_ZOOM: + os << " zoom=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_ZPOS: + os << " cam_zpos=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_PERSPECTIVE: + os << " cam_persp=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_YAW: + os << " cam_yaw=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_PITCH: + os << " cam_pitch=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_DEPTH_BLUR: + os << " cam_dof=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_CENTER_X: + cx = motion.m_MotionParams[i].second; + break; + case FLAME_MOTION_CENTER_Y: + cy = motion.m_MotionParams[i].second; + break; + case FLAME_MOTION_ROTATE: + os << " rotate=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_HUE: + os << " hue=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_BRIGHTNESS: + os << " brightness=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_GAMMA: + os << " gamma=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_GAMMA_THRESH: + os << " gamma_threshold=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_HIGHLIGHT_POWER: + os << " highlight_power=\"" << motion.m_MotionParams[i].second << "\""; + break; + case FLAME_MOTION_BACKGROUND_R: + r = motion.m_MotionParams[i].second; + break; + case FLAME_MOTION_BACKGROUND_G: + g = motion.m_MotionParams[i].second; + break; + case FLAME_MOTION_BACKGROUND_B: + b = motion.m_MotionParams[i].second; + break; + case FLAME_MOTION_VIBRANCY: + os << " vibrancy=\"" << motion.m_MotionParams[i].second << "\""; + 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) 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/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 d1d8c58..07c3d2f 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -1010,11 +1010,11 @@ 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 ember, at the xform level and at the parent ember level. } /// diff --git a/Source/Ember/Xform.h b/Source/Ember/Xform.h index 4e94319..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) /// @@ -731,8 +734,9 @@ 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; + 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) { @@ -1232,7 +1237,8 @@ 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; + T m_MotionOffset; vector> m_Motion; string m_Name; diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 07ea62f..9612876 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); } } } @@ -971,7 +978,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 (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"); } @@ -998,6 +1005,117 @@ private: editNode = xmlCopyNode(childNode, 1); xmlDocSetRootElement(currentEmber.m_Edits, editNode); } + else if (!Compare(childNode->name, "flame_motion")) + { + EmberMotion 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 (ParseAndAssignFloat(curAtt->name, attStr, "motion_offset", motion.m_MotionOffset, 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 && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_ZOOM, motion); + else if (!Compare(curAtt->name, "cam_zpos")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_ZPOS, motion); + else if (!Compare(curAtt->name, "cam_persp")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_PERSPECTIVE, motion); + else if (!Compare(curAtt->name, "cam_yaw")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_YAW, motion); + else if (!Compare(curAtt->name, "cam_pitch")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_PITCH, motion); + else if (!Compare(curAtt->name, "cam_dof")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_DEPTH_BLUR, motion); + else if (!Compare(curAtt->name, "rotate")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_ROTATE, motion); + else if (!Compare(curAtt->name, "hue")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_HUE, motion); + else if (!Compare(curAtt->name, "brightness")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_BRIGHTNESS, motion); + else if (!Compare(curAtt->name, "gamma")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_GAMMA, motion); + else if (!Compare(curAtt->name, "gamma_threshold")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_GAMMA_THRESH, motion); + else if (!Compare(curAtt->name, "highlight_power")) + ret = ret && AttToEmberMotionFloat(att, attStr, FLAME_MOTION_HIGHLIGHT_POWER, motion); + else if (!Compare(curAtt->name, "vibrancy")) + ret = ret && AttToEmberMotionFloat(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(MotionParam(FLAME_MOTION_BACKGROUND_R, T(r))); + + if (g != 0) + motion.m_MotionParams.push_back(MotionParam(FLAME_MOTION_BACKGROUND_G, T(g))); + + if (b != 0) + motion.m_MotionParams.push_back(MotionParam(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(MotionParam(FLAME_MOTION_CENTER_X, T(cx))); + + if (cy != 0) + motion.m_MotionParams.push_back(MotionParam(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_EmberMotionElements.push_back(motion); + } } //if (!newLinear) @@ -1010,6 +1128,33 @@ private: return m_ErrorReport.empty(); } + /// + /// 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 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(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)); + } + + return r; + } + /// /// Parse an xform element. /// @@ -1047,9 +1192,8 @@ 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)) { } + 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")) @@ -1073,6 +1217,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; 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/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h index 047af08..87e2790 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.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")); @@ -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..d62bc17 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) @@ -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()); } } 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