Nicolas Winkler 5 years ago
parent
commit
a91b871b33
15 changed files with 248 additions and 67 deletions
  1. 6 1
      Almond.cpp
  2. 1 7
      Almond.h
  3. 3 0
      Almond.pro
  4. 7 0
      Almond.ui
  5. 22 0
      Color.cpp
  6. 39 1
      Color.h
  7. 66 0
      CubicSpline.cpp
  8. 18 0
      CubicSpline.h
  9. 56 41
      Gradient.cpp
  10. 3 2
      Gradient.h
  11. 16 5
      MandelWidget.cpp
  12. 1 0
      MandelWidget.h
  13. 8 9
      benchmarkdialog.cpp
  14. 1 0
      gradients/default.xml
  15. 1 1
      libmandel/src/ClGenerators.cpp

+ 6 - 1
Almond.cpp

@@ -101,7 +101,7 @@ void Almond::on_exportImage_clicked()
         mi.bWidth = dialog.getWidth();
         mi.bHeight = dialog.getHeight();
         mi.view.adjustAspectRatio(mi.bWidth, mi.bHeight);
-        mnd::Generator& g = mandelContext.getCpuGeneratorFloat();
+        mnd::Generator& g = mandelContext.getDefaultGenerator(this->mw->getSmoothColoring());
         auto fmap = Bitmap<float>(mi.bWidth, mi.bHeight);
         g.generate(mi, fmap.pixels.get());
         auto bitmap = fmap.map<RGBColor>([&mi, this] (float i) {
@@ -111,3 +111,8 @@ void Almond::on_exportImage_clicked()
         img.save(dialog.getPath());
     }
 }
+
+void Almond::on_resetZoom_clicked()
+{
+    mw->setViewport(mnd::MandelViewport::standardView());
+}

+ 1 - 7
Almond.h

@@ -22,20 +22,14 @@ public:
 
 private slots:
     void on_zoom_out_clicked();
-
     void on_zoom_in_clicked();
-
     void on_maxIterations_editingFinished();
-
     void on_chooseGradient_clicked();
-
     void on_exportVideo_clicked();
-
     void on_smooth_stateChanged(int arg1);
-
     void on_runBenchmark_clicked();
-
     void on_exportImage_clicked();
+    void on_resetZoom_clicked();
 
 private:
     Ui::AlmondClass ui;

+ 3 - 0
Almond.pro

@@ -28,6 +28,8 @@ CONFIG += c++17
 SOURCES += \
         Almond.cpp \
         Bitmap.cpp \
+        Color.cpp \
+        CubicSpline.cpp \
         Gradient.cpp \
         MandelVideoGenerator.cpp \
         MandelWidget.cpp \
@@ -41,6 +43,7 @@ HEADERS += \
         Almond.h \
         Bitmap.h \
         Color.h \
+        CubicSpline.h \
         Gradient.h \
         MandelVideoGenerator.h \
         MandelWidget.h \

+ 7 - 0
Almond.ui

@@ -107,6 +107,13 @@
          </widget>
         </item>
         <item>
+         <widget class="QPushButton" name="resetZoom">
+          <property name="text">
+           <string>Reset Zoom</string>
+          </property>
+         </widget>
+        </item>
+        <item>
          <spacer name="verticalSpacer_2">
           <property name="orientation">
            <enum>Qt::Vertical</enum>

+ 22 - 0
Color.cpp

@@ -0,0 +1,22 @@
+#include "Color.h"
+#include <cmath>
+#include <algorithm>
+
+
+RGBColor::RGBColor(const RGBColorf& rgb)
+{
+    float cr = std::clamp(rgb.r, 0.0f, 1.0f);
+    float cg = std::clamp(rgb.g, 0.0f, 1.0f);
+    float cb = std::clamp(rgb.b, 0.0f, 1.0f);
+    r = uint8_t(cr * cr * 255.0f);
+    g = uint8_t(cg * cg * 255.0f);
+    b = uint8_t(cb * cb * 255.0f);
+}
+
+
+RGBColorf::RGBColorf(const RGBColor& srgb)
+{
+    r = ::sqrtf(srgb.r / 255.0f);
+    g = ::sqrtf(srgb.g / 255.0f);
+    b = ::sqrtf(srgb.b / 255.0f);
+}

+ 39 - 1
Color.h

@@ -2,9 +2,47 @@
 #ifndef COLOR_H_
 #define COLOR_H_
 
+#include <cinttypes>
+
+
+struct RGBColor;
+struct RGBColorf;
+
+/*!
+ * \brief Represents a color in the sRGB color space with 8-bit channels
+ */
 struct RGBColor
 {
-    unsigned char r, g, b;
+    uint8_t r, g, b;
+
+    inline RGBColor(void) :
+        r{ 0 }, g{ 0 }, b{ 0 }
+    {
+    }
+
+    inline RGBColor(uint8_t r, uint8_t g, uint8_t b) :
+        r{ r }, g{ g }, b{ b }
+    {
+    }
+
+    RGBColor(const RGBColorf& rgb);
+};
+
+
+/*!
+ * \brief Represents a color in a linear RGB color space with 32-bit floating
+ *        point numbers as channels.
+ */
+struct RGBColorf
+{
+    float r, g, b;
+
+    inline RGBColorf(float r, float g, float b) :
+        r{ r }, g{ g }, b{ b }
+    {
+    }
+
+    RGBColorf(const RGBColor& srgb);
 };
 
 #endif // COLOR_H_

+ 66 - 0
CubicSpline.cpp

@@ -0,0 +1,66 @@
+#include "CubicSpline.h"
+
+CubicSpline::CubicSpline(const std::vector<std::pair<float, float> >& dataPoints, bool useSlopes) :
+    useSlopes{ useSlopes }
+{
+    if (dataPoints.size() < 2) {
+        return;
+    }
+
+    points.push_back({ dataPoints[0].first, dataPoints[0].second,
+                       (dataPoints[1].second - dataPoints[0].second) /
+                       (dataPoints[1].first - dataPoints[0].first) });
+    for (size_t i = 1; i < dataPoints.size() - 1; i++) {
+
+        auto& dp1 = dataPoints[i - 1];
+        auto& dp2 = dataPoints[i];
+        auto& dp3 = dataPoints[i + 1];
+
+        float w1 = dp2.first - dp1.first;
+        float w2 = dp3.first - dp2.first;
+        float h1 = dp2.second - dp1.second;
+        float h2 = dp3.second - dp2.second;
+
+        float s1 = h1 / w1;
+        float s2 = h2 / w2;
+
+        float avgSlope = (s1 + s2) / 2;
+        points.push_back({ dp2.first, dp2.second, avgSlope });
+    }
+    points.push_back({ dataPoints[dataPoints.size() - 1].first, dataPoints[dataPoints.size() - 1].second,
+                       (dataPoints[dataPoints.size() - 2].second - dataPoints[dataPoints.size() - 1].second) /
+                       (dataPoints[dataPoints.size() - 2].first - dataPoints[dataPoints.size() - 1].first) });
+}
+
+
+float CubicSpline::interpolateAt(float x)
+{
+    const static auto h00 = [] (float t) { return (1 + 2 * t) * (1 - t) * (1 - t); };
+    const static auto h01 = [] (float t) { return t * t * (3 - 2 * t); };
+    const static auto h10 = [] (float t) { return t * (1 - t) * (1 - t); };
+    const static auto h11 = [] (float t) { return t * t * (t - 1); };
+    for (auto it = points.begin(); it != points.end() && (it + 1) != points.end(); ++it) {
+        auto& left = *it;
+        auto& right = *(it + 1);
+        float xleft = std::get<0>(left);
+        float xright = std::get<0>(right);
+        if (xleft < x && xright >= x) {
+            float w = (xright - xleft);
+            float t = (x - xleft) / w;
+            float yleft = std::get<1>(left);
+            float yright = std::get<1>(right);
+            float sleft = std::get<2>(left);
+            float sright = std::get<2>(right);
+
+            float inter = h00(t) * yleft +
+                          h01(t) * yright;
+
+            if (useSlopes)
+                inter += h10(t) * w * sleft +
+                         h11(t) * w * sright;
+
+            return inter;
+        }
+    }
+    return std::get<1>(points[points.size() - 1]);
+}

+ 18 - 0
CubicSpline.h

@@ -0,0 +1,18 @@
+#ifndef CUBICSPLINE_H
+#define CUBICSPLINE_H
+
+#include <vector>
+#include <utility>
+
+class CubicSpline
+{
+    /// contains x, y and y' of each interpolation point
+    std::vector<std::tuple<float, float, float>> points;
+    bool useSlopes;
+public:
+    CubicSpline(const std::vector<std::pair<float, float>>& dataPoints, bool useSlopes);
+
+    float interpolateAt(float x);
+};
+
+#endif // CUBICSPLINE_H

+ 56 - 41
Gradient.cpp

@@ -1,7 +1,10 @@
 #include "Gradient.h"
 
+#include "CubicSpline.h"
+
 #include <cmath>
 #include <algorithm>
+#include <functional>
 #include <QtXml/QDomDocument>
 #include <QFile>
 
@@ -12,38 +15,49 @@ Gradient::Gradient(void) :
 }
 
 
-Gradient::Gradient(const std::vector<std::pair<RGBColor, float>>& colors, bool repeat, int precalcSteps) :
+Gradient::Gradient(std::vector<std::pair<RGBColor, float>> colors, bool repeat, int precalcSteps) :
     repeat{ repeat }
 {
-    if(colors.empty())
+    if(colors.empty() || colors.size() < 2)
         return;
-    /*std::sort(colors.begin(), colors.end(),
-              [] (const std::pair<RGBColor, float>& a, const std::pair<RGBColor, float>& b) {
-        return a.second < b.second;
-    });*/
+    std::sort(colors.begin(), colors.end(),
+        [] (const auto& a, const auto& b) {
+            return a.second < b.second;
+        });
 
     max = colors.at(colors.size() - 1).second;
+
+    std::vector<std::pair<RGBColorf, float>> linearColors;
+    std::transform(colors.begin(), colors.end(), std::back_inserter(linearColors),
+                   [] (auto c) { return c; });
+
+    std::vector<std::pair<float, float>> rs;
+    std::vector<std::pair<float, float>> gs;
+    std::vector<std::pair<float, float>> bs;
+
+    std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(rs),
+                   [] (auto p) { return std::pair{ p.second, p.first.r }; });
+    std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(gs),
+                   [] (auto p) { return std::pair{ p.second, p.first.g }; });
+    std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(bs),
+                   [] (auto p) { return std::pair{ p.second, p.first.b }; });
+
+    CubicSpline rsp(rs, false);
+    CubicSpline gsp(gs, false);
+    CubicSpline bsp(bs, false);
+
+    if(precalcSteps <= 0) {
+        precalcSteps = int(max * 15) + 10;
+    }
+
     for (int i = 0; i < precalcSteps; i++) {
         float position = i * max / precalcSteps;
-        RGBColor left = RGBColor{ 0, 0, 0 };
-        RGBColor right = RGBColor{ 0, 0, 0 };
-        float lerp = 0.0f;
-        RGBColor atPosition = RGBColor{ 0, 0, 0 };
-        // slow, but not in any critical path
-        for (auto it = colors.begin(); it != colors.end(); ++it) {
-            if (it->second > position) {
-                if (it == colors.begin()) {
-                    atPosition = it->first;
-                    break;
-                }
-                else {
-                    float lerp = (position - (it - 1)->second) / (it->second - (it - 1)->second);
-                    atPosition = lerpColors((it - 1)->first, it->first, lerp);
-                    break;
-                }
-            }
-        }
-        this->colors.push_back(atPosition);
+        RGBColorf at = {
+            rsp.interpolateAt(position),
+            gsp.interpolateAt(position),
+            bsp.interpolateAt(position)
+        };
+        this->colors.push_back(at);
     }
 }
 
@@ -67,16 +81,16 @@ Gradient Gradient::readXml(const QString& xml)
     std::vector<std::pair<RGBColor, float>> colorArr;
     for (int i = 0; i < colors.length(); ++i) {
         auto child = colors.item(i).toElement();
-        uint8_t r = child.attributeNode("r").value().toInt();
-        uint8_t g = child.attributeNode("g").value().toInt();
-        uint8_t b = child.attributeNode("b").value().toInt();
+        uint8_t r = uint8_t(child.attributeNode("r").value().toInt());
+        uint8_t g = uint8_t(child.attributeNode("g").value().toInt());
+        uint8_t b = uint8_t(child.attributeNode("b").value().toInt());
         float p = child.attributeNode("p").value().toInt();
 
         printf("rgb (%s): %d, %d, %d\n", child.text().toUtf8().data(), r, g, b);
         colorArr.push_back({ { r, g, b }, p });
     }
 
-    return Gradient(colorArr, repeat);
+    return Gradient(std::move(colorArr), repeat);
 }
 
 
@@ -88,6 +102,8 @@ RGBColor Gradient::get(float x) const
     RGBColor lerped = lerpColors(left, right, lerp);
     return lerped;*/
 
+    if (x < 0)
+        return colors[0];
     if (x > this->max) {
         if (repeat)
             x = ::fmodf(x, this->max);
@@ -104,7 +120,7 @@ RGBColor Gradient::get(float x) const
     int right = int(pos + 1);
     float lerp = pos - left;
 
-    if (lerp < 1e-5) {
+    if (lerp < 1e-5f) {
         return colors.at(left);
     }
     else {
@@ -113,20 +129,19 @@ RGBColor Gradient::get(float x) const
 }
 
 
-RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
+RGBColorf Gradient::lerpColors(RGBColorf a, RGBColorf b, float val)
 {
-    auto mklin = [] (double x) {
-        return x * x;//::pow(x, 2.4);
-    };
-    auto unlin = [] (double x) {
-        return ::sqrt(x);// ::pow(x, 1.0 / 2.4);
+    return RGBColorf {
+        b.r * val + a.r * (1 - val),
+        b.g * val + a.g * (1 - val),
+        b.b * val + a.b * (1 - val)
     };
+}
 
-    return RGBColor{
-        uint8_t(unlin(mklin(b.r) * val + mklin(a.r) * (1 - val))),
-        uint8_t(unlin(mklin(b.g) * val + mklin(a.g) * (1 - val))),
-        uint8_t(unlin(mklin(b.b) * val + mklin(a.b) * (1 - val)))
-    };
+
+RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
+{
+    return RGBColor{ lerpColors(RGBColorf{ a }, RGBColorf{ b }, val) };
 }
 
 

+ 3 - 2
Gradient.h

@@ -12,12 +12,12 @@ class Gradient
 {
     /// the colors of this gradient stored in linear RGB format
     /// so they can be easily interpolated
-    std::vector<RGBColor> colors;
+    std::vector<RGBColorf> colors;
     float max;
     bool repeat;
 public:
     Gradient(void);
-    Gradient(const std::vector<std::pair<RGBColor, float>>&, bool repeat = false, int precalcSteps = 10000);
+    Gradient(std::vector<std::pair<RGBColor, float>> colors, bool repeat = false, int precalcSteps = -1);
 
     static Gradient defaultGradient(void);
 
@@ -30,6 +30,7 @@ public:
      */
     RGBColor get(float x) const;
 private:
+    static RGBColorf lerpColors(RGBColorf a, RGBColorf b, float val);
     static RGBColor lerpColors(RGBColor a, RGBColor b, float val);
     std::tuple<RGBColor, RGBColor, float> getNeighbors(float x) const;
 };

+ 16 - 5
MandelWidget.cpp

@@ -341,10 +341,12 @@ TexGrid& MandelView::getGrid(int level)
 
 void MandelView::setMaxIter(int maxIter)
 {
-    this->maxIter = maxIter;
-    calcer.setMaxIter(maxIter);
-    clearCells();
-    emit redrawRequested();
+    if (this->maxIter != maxIter) {
+        this->maxIter = maxIter;
+        calcer.setMaxIter(maxIter);
+        clearCells();
+        emit redrawRequested();
+    }
 }
 
 
@@ -693,12 +695,21 @@ void MandelWidget::zoom(float scale, float x, float y)
 }
 
 
+void MandelWidget::setViewport(const mnd::MandelViewport& viewport)
+{
+    targetViewport = viewport;
+    currentViewport = viewport;
+    //lastAnimUpdate = std::chrono::high_resolution_clock::now();
+    //currentViewport.zoom(scale, x, y);
+    requestRecalc();
+}
+
+
 void MandelWidget::setMaxIterations(int maxIter)
 {
     this->maxIterations = maxIter;
     if (mandelView)
         mandelView->setMaxIter(maxIter);
-    requestRecalc();
 }
 
 

+ 1 - 0
MandelWidget.h

@@ -326,6 +326,7 @@ private:
 public:
 
     void zoom(float scale, float x = 0.5f, float y = 0.5f);
+    void setViewport(const mnd::MandelViewport& viewport);
     void setMaxIterations(int maxIter);
 
     //void redraw();

+ 8 - 9
benchmarkdialog.cpp

@@ -110,14 +110,14 @@ void Benchmarker::start(void)
     double nTests = 3;
 
     auto& devices = mndContext.getDevices();
-    for (int i = 0; i < devices.size(); i++) {
-        if (mnd::Generator* gpuf; gpuf = devices[i].getGeneratorFloat()) {
+    for (size_t i = 0; i < devices.size(); i++) {
+        if (mnd::Generator* gpuf; (gpuf = devices[i].getGeneratorFloat())) {
             nTests++;
         }
-        if (mnd::Generator* gpud; gpud = devices[i].getGeneratorDouble()) {
+        if (mnd::Generator* gpud; (gpud = devices[i].getGeneratorDouble())) {
             nTests++;
         }
-        if (mnd::Generator* gpu128; gpu128 = devices[i].getGenerator128()) {
+        if (mnd::Generator* gpu128; (gpu128 = devices[i].getGenerator128())) {
             nTests++;
         }
     }
@@ -141,26 +141,25 @@ void Benchmarker::start(void)
     br.percentage += progress;
     emit update(br);
 
-    for (int i = 0; i < devices.size(); i++) {
+    for (size_t i = 0; i < devices.size(); i++) {
         br.values.push_back({});
         std::vector<double>& gpu = br.values[br.values.size() - 1];
-        if (mnd::Generator* gpuf; gpuf = devices[i].getGeneratorFloat()) {
+        if (mnd::Generator* gpuf; (gpuf = devices[i].getGeneratorFloat())) {
             gpu.push_back(benchmarkResult(*gpuf));
             br.percentage += progress;
             emit update(br);
         }
-        if (mnd::Generator* gpud; gpud = devices[i].getGeneratorDouble()) {
+        if (mnd::Generator* gpud; (gpud = devices[i].getGeneratorDouble())) {
             gpu.push_back(benchmarkResult(*gpud));
             br.percentage += progress;
             emit update(br);
         }
-        if (mnd::Generator* gpu128; gpu128 = devices[i].getGenerator128()) {
+        if (mnd::Generator* gpu128; (gpu128 = devices[i].getGenerator128())) {
             gpu.push_back(benchmarkResult(*gpu128));
             br.percentage += progress;
             emit update(br);
         }
     }
-    printf("benchmark finished\n");
     emit update(br);
     emit finished();
 }

+ 1 - 0
gradients/default.xml

@@ -1,5 +1,6 @@
 <gradient repeat="true">
     <color r="0" g="0" b="0" p="0" />
+    <color r="125" g="50" b="15" p="20" />
     <color r="250" g="70" b="24" p="40" />
     <color r="200" g="230" b="30" p="80" />
     <color r="70" g="223" b="30" p="105" />

+ 1 - 1
libmandel/src/ClGenerators.cpp

@@ -233,7 +233,7 @@ void ClGeneratorDouble::generate(const mnd::MandelInfo& info, float* data)
     iterate.setArg(5, double(pixelScaleY));
     iterate.setArg(6, int(info.maxIter));
 
-    queue.enqueueNDRangeKernel(iterate, 0, NDRange(info.bWidth * info.bHeight));
+    cl_int result = queue.enqueueNDRangeKernel(iterate, 0, NDRange(info.bWidth * info.bHeight));
     queue.enqueueReadBuffer(buffer_A, CL_TRUE, 0, bufferSize, data);
 }