Selaa lähdekoodia

julia sets added

Nicolas Winkler 5 vuotta sitten
vanhempi
commit
51a5c20d56

+ 51 - 6
Almond.cpp

@@ -12,8 +12,17 @@ Almond::Almond(QWidget* parent) :
     mandelContext{ mnd::initializeContext() }
 {
     ui.setupUi(this);
-    currentGenerator = &mandelContext.getDefaultGenerator();
-    mw = std::make_unique<MandelWidget>(mandelContext, currentGenerator, ui.centralWidget);
+
+
+    mw = std::make_unique<MandelWidget>(mandelContext,
+                                        &mandelContext.getDefaultGenerator(),
+                                        ui.centralWidget);
+
+    currentView = MANDELBROT;
+    mandelGeneratorSave = &mandelContext.getDefaultGenerator();
+    mandelViewSave = mw->getViewport();
+
+    QObject::connect(mw.get(), &MandelWidget::pointSelected, this, &Almond::pointSelected);
     ui.mainContainer->addWidget(mw.get());
     ui.maxIterations->setValidator(new QIntValidator(1, 1000000000, this));
     ui.backgroundProgress->setVisible(false);
@@ -119,7 +128,13 @@ void Almond::on_exportImage_clicked()
         mi.bWidth = dialog.getWidth();
         mi.bHeight = dialog.getHeight();
         mi.view.adjustAspectRatio(mi.bWidth, mi.bHeight);
-        mnd::Generator& g = currentGenerator ? *currentGenerator : mandelContext.getDefaultGenerator();
+        mi.smooth = mw->getSmoothColoring();
+        if (currentView == JULIA) {
+            mi.juliaX = mw->getJuliaX();
+            mi.juliaY = mw->getJuliaY();
+        }
+        mnd::MandelGenerator* currentGenerator = mw->getGenerator();
+        mnd::MandelGenerator& g = currentGenerator ? *currentGenerator : mandelContext.getDefaultGenerator();
         auto fmap = Bitmap<float>(mi.bWidth, mi.bHeight);
         g.generate(mi, fmap.pixels.get());
         auto bitmap = fmap.map<RGBColor>([&mi, this] (float i) {
@@ -150,11 +165,41 @@ void Almond::on_chooseGenerator_clicked()
     generatorsDialog->exec();
 
     if (generatorsDialog->getChosenGenerator()) {
-        this->currentGenerator = generatorsDialog->getChosenGenerator();
+        mandelGeneratorSave = generatorsDialog->getChosenGenerator();
     }
     else {
-        this->currentGenerator = &mandelContext.getDefaultGenerator();
+        mandelGeneratorSave = &mandelContext.getDefaultGenerator();
     }
-    this->mw->setGenerator(currentGenerator);
+    this->currentView = MANDELBROT;
+    this->mw->setGenerator(mandelGeneratorSave);
     printf("dialog executed\n"); fflush(stdout);
 }
+
+void Almond::on_selectPoint_clicked()
+{
+    if (currentView == MANDELBROT) {
+        emit this->mw->selectPoint();
+    }
+}
+
+
+void Almond::pointSelected(mnd::Real x, mnd::Real y)
+{
+    if (currentView != JULIA) {
+        auto& gen = mandelContext.getJuliaGenerator();
+        mandelViewSave = mw->getViewport();
+        this->mw->setGenerator(&gen);
+        this->mw->setViewport(mnd::MandelViewport::centerView());
+        this->mw->setJuliaPos(x, y);
+    }
+    currentView = JULIA;
+}
+
+void Almond::on_viewMandelbrot_clicked()
+{
+    if (currentView != MANDELBROT) {
+        this->mw->setGenerator(mandelGeneratorSave);
+        this->mw->setViewport(mandelViewSave);
+        currentView = MANDELBROT;
+    }
+}

+ 23 - 1
Almond.h

@@ -13,6 +13,20 @@
 
 #include <memory>
 
+struct ViewState
+{
+    mnd::MandelViewport viewport;
+    mnd::MandelGenerator* generator;
+};
+
+
+enum ViewType
+{
+    MANDELBROT,
+    JULIA
+};
+
+
 class Almond : public QMainWindow
 {
     Q_OBJECT
@@ -21,8 +35,11 @@ private:
     std::unique_ptr<MandelWidget> mw;
     //std::unique_ptr<BenchmarkDialog> benchmarkDialog;
     std::unique_ptr<ChooseGenerators> generatorsDialog;
-    mnd::Generator* currentGenerator;
     GradientChooseDialog gcd;
+
+    ViewType currentView;
+    mnd::MandelViewport mandelViewSave;
+    mnd::MandelGenerator* mandelGeneratorSave;
 public:
     Almond(QWidget *parent = Q_NULLPTR);
     ~Almond(void);
@@ -45,6 +62,11 @@ private slots:
 
     void backgroundTaskFinished();
 
+    void on_selectPoint_clicked();
+    void pointSelected(mnd::Real x, mnd::Real y);
+
+    void on_viewMandelbrot_clicked();
+
 private:
     Ui::AlmondClass ui;
 };

+ 29 - 2
Almond.ui

@@ -9,8 +9,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>763</width>
-    <height>524</height>
+    <width>950</width>
+    <height>665</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -150,6 +150,33 @@
         </widget>
        </item>
        <item>
+        <spacer name="verticalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QPushButton" name="selectPoint">
+         <property name="text">
+          <string>View Julia Set</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="viewMandelbrot">
+         <property name="text">
+          <string>View Mandelbrot Set</string>
+         </property>
+        </widget>
+       </item>
+       <item>
         <spacer name="verticalSpacer_2">
          <property name="orientation">
           <enum>Qt::Vertical</enum>

+ 1 - 1
MandelVideoGenerator.cpp

@@ -12,7 +12,7 @@ MandelVideoGenerator::MandelVideoGenerator(const ExportVideoInfo& evi) :
 void MandelVideoGenerator::generate(void)
 {
     mnd::MandelContext ctxt = mnd::initializeContext();
-    mnd::Generator& gen = ctxt.getDefaultGenerator();
+    mnd::MandelGenerator& gen = ctxt.getDefaultGenerator();
     mnd::MandelInfo mi;
     mi.bWidth = evi.width * 2;
     mi.bHeight = evi.height * 2;

+ 60 - 7
MandelWidget.cpp

@@ -228,6 +228,8 @@ void Job::run(void)
     mi->bWidth = mi->bHeight = MandelView::chunkSize;
     mi->maxIter = owner.getMaxIterations();
     mi->smooth = owner.getSmoothColoring();
+    mi->juliaX = juliaX;
+    mi->juliaY = juliaY;
     try {
         generator->generate(*mi, f.pixels.get());
         auto* rgb = new Bitmap<RGBColor>(f.map<RGBColor>([&mi, this](float i) {
@@ -246,11 +248,11 @@ void Job::run(void)
 }
 
 
-Calcer::Calcer(mnd::Generator* generator, MandelWidget& owner, int maxIter, bool smooth) :
+Calcer::Calcer(mnd::MandelGenerator* generator, MandelWidget& owner, int maxIter, bool smooth) :
     jobsMutex{ QMutex::Recursive },
     generator{ generator },
-    owner{ owner },
     threadPool{ std::make_unique<QThreadPool>() },
+    owner{ owner },
     gradient{ owner.getGradient() },
     maxIter{ maxIter },
     smooth{ smooth }
@@ -273,11 +275,19 @@ void Calcer::clearAll(void)
 }
 
 
+void Calcer::setJuliaPos(const mnd::Real& x, const mnd::Real& y)
+{
+    this->juliaX = x;
+    this->juliaY = y;
+    changeState();
+}
+
+
 void Calcer::calc(TexGrid& grid, int level, GridIndex i, GridIndex j, int priority)
 {
     jobsMutex.lock();
     if (jobs.find({ level, i, j }) == jobs.end()) {
-        Job* job = new Job(generator, gradient, owner, &grid, level, i, j, calcState);
+        Job* job = new Job(generator, gradient, owner, &grid, level, i, j, juliaX, juliaY, calcState);
         connect(job, &Job::done, this, &Calcer::redirect);
         connect(job, &QObject::destroyed, this, [this, level, i, j] () { this->notFinished(level, i, j); });
         jobs.emplace(std::tuple{level, i, j}, job);
@@ -333,7 +343,7 @@ void Calcer::redirect(int level, GridIndex i, GridIndex j, long calcState, Bitma
 
 const int MandelView::chunkSize = 256;
 
-MandelView::MandelView(mnd::Generator* generator, MandelWidget& owner, int maxIter) :
+MandelView::MandelView(mnd::MandelGenerator* generator, MandelWidget& owner, int maxIter) :
     generator{ generator },
     calcer{ generator, owner, maxIter, owner.getSmoothColoring() },
     owner{ owner },
@@ -390,7 +400,7 @@ void MandelView::setMaxIter(int maxIter)
 }
 
 
-void MandelView::setGenerator(mnd::Generator* generator)
+void MandelView::setGenerator(mnd::MandelGenerator* generator)
 {
     if (this->generator != generator) {
         this->generator = generator;
@@ -581,7 +591,7 @@ void MandelView::cellReady(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>
 }
 
 
-MandelWidget::MandelWidget(mnd::MandelContext& ctxt, mnd::Generator* generator, QWidget* parent) :
+MandelWidget::MandelWidget(mnd::MandelContext& ctxt, mnd::MandelGenerator* generator, QWidget* parent) :
     QOpenGLWidget{ parent },
     mndContext{ ctxt },
     generator{ generator },
@@ -650,7 +660,14 @@ void MandelWidget::setMaxIterations(int maxIter)
 }
 
 
-void MandelWidget::setGenerator(mnd::Generator* generator)
+void MandelWidget::setJuliaPos(const mnd::Real& x, const mnd::Real& y)
+{
+    if (mandelView)
+        mandelView->calcer.setJuliaPos(x, y);
+}
+
+
+void MandelWidget::setGenerator(mnd::MandelGenerator* generator)
 {
     if (this->generator != generator) {
         this->generator = generator;
@@ -710,6 +727,8 @@ void MandelWidget::paintGL(void)
         drawRubberband();
     if (displayInfo)
         drawInfo();
+    if (selectingPoint)
+        drawPoint();
 }
 
 
@@ -806,6 +825,19 @@ void MandelWidget::drawInfo(void)
 }
 
 
+void MandelWidget::drawPoint(void)
+{
+    glColor3ub(255, 255, 255);
+    glBegin(GL_LINES);
+    glVertex2f(0, pointY);
+    glVertex2f(width(), pointY);
+
+    glVertex2f(pointX, 0);
+    glVertex2f(pointX, height());
+    glEnd();
+}
+
+
 void MandelWidget::zoom(float scale, float x, float y)
 {
     targetViewport.zoom(scale, x, y);
@@ -826,6 +858,13 @@ void MandelWidget::setViewport(const mnd::MandelViewport& viewport)
 }
 
 
+void MandelWidget::selectPoint(void)
+{
+    this->selectingPoint = true;
+    this->setMouseTracking(true);
+}
+
+
 void MandelWidget::requestRecalc()
 {
     emit update();
@@ -869,6 +908,7 @@ void MandelWidget::mousePressEvent(QMouseEvent* me)
 void MandelWidget::mouseMoveEvent(QMouseEvent* me)
 {
     QOpenGLWidget::mouseMoveEvent(me);
+
     if (rubberbanding) {
         QRectF& rect = rubberband;
         double aspect = double(geometry().width()) / geometry().height();
@@ -880,6 +920,11 @@ void MandelWidget::mouseMoveEvent(QMouseEvent* me)
 
         emit repaint();
     }
+    else if (selectingPoint) {
+        pointX = me->x();
+        pointY = me->y();
+        emit repaint();
+    }
     else if (dragging) {
         double deltaX = me->x() - dragX;
         double deltaY = me->y() - dragY;
@@ -913,6 +958,14 @@ void MandelWidget::mouseReleaseEvent(QMouseEvent* me)
         requestRecalc();
         rubberbanding = false;
     }
+    else if (selectingPoint) {
+        selectingPoint = false;
+        this->setMouseTracking(false);
+        mnd::Real x = currentViewport.x + currentViewport.width * mnd::convert<mnd::Real>(float(me->x()) / width());
+        mnd::Real y = currentViewport.y + currentViewport.height * mnd::convert<mnd::Real>(float(me->y()) / height());
+        emit pointSelected(x, y);
+        emit repaint();
+    }
     dragging = false;
 
     //requestRecalc();

+ 32 - 12
MandelWidget.h

@@ -25,6 +25,7 @@
 
 using GridIndex = mnd::Integer;
 Q_DECLARE_METATYPE(GridIndex)
+Q_DECLARE_METATYPE(mnd::Real)
 
 
 class MandelView;
@@ -174,19 +175,22 @@ class Job : public QObject, public QRunnable
 {
     Q_OBJECT
 public:
-    mnd::Generator* generator;
+    mnd::MandelGenerator* generator;
     const Gradient& gradient;
     MandelWidget& owner;
     TexGrid* grid;
     int level;
     GridIndex i, j;
+    mnd::Real juliaX;
+    mnd::Real juliaY;
     long calcState = 0;
 
-    inline Job(mnd::Generator* generator,
+    inline Job(mnd::MandelGenerator* generator,
                const Gradient& gradient,
                MandelWidget& owner,
                TexGrid* grid,
                int level, GridIndex i, GridIndex j,
+               const mnd::Real& juliaX, const mnd::Real& juliaY,
                long calcState) :
         generator{ generator },
         gradient{ gradient },
@@ -194,6 +198,7 @@ public:
         grid{ grid },
         level{ level },
         i{ i }, j{ j },
+        juliaX{ juliaX }, juliaY{ juliaY },
         calcState{ calcState }
     {}
 
@@ -209,7 +214,11 @@ class Calcer : public QObject
     /// tuple contains level, i, j of the job
     std::unordered_map<std::tuple<int, GridIndex, GridIndex>, Job*, TripleHash> jobs;
     QMutex jobsMutex;
-    mnd::Generator* generator;
+    mnd::MandelGenerator* generator;
+public:
+    mnd::Real juliaX;
+    mnd::Real juliaY;
+private:
     std::unique_ptr<QThreadPool> threadPool;
     MandelWidget& owner;
     const Gradient& gradient;
@@ -219,10 +228,11 @@ class Calcer : public QObject
 
     volatile long calcState = 0;
 public:
-    Calcer(mnd::Generator* generator, MandelWidget& owner, int maxIter, bool smooth);
+    Calcer(mnd::MandelGenerator* generator, MandelWidget& owner, int maxIter, bool smooth);
     void setMaxIter(int maxIter);
     void clearAll(void);
-    void setGenerator(mnd::Generator* generator) { this->generator = generator; changeState(); }
+    void setGenerator(mnd::MandelGenerator* generator) { this->generator = generator; changeState(); }
+    void setJuliaPos(const mnd::Real& x, const mnd::Real& y);
 
     inline void changeState(void) { calcState++; }
 
@@ -245,7 +255,7 @@ public:
     // a grid should not be deleted once constructed.
     // to free up memory one can call TexGrid::clearCells()
     std::unordered_map<int, TexGrid> levels;
-    mnd::Generator* generator;
+    mnd::MandelGenerator* generator;
     Calcer calcer;
     MandelWidget& owner;
     int maxIter;
@@ -253,7 +263,7 @@ public:
     int height;
 public:
     static const int chunkSize;
-    MandelView(mnd::Generator* generator, MandelWidget& owner, int maxIter);
+    MandelView(mnd::MandelGenerator* generator, MandelWidget& owner, int maxIter);
     int getLevel(mnd::Real dpp);
     mnd::Real getDpp(int level);
 
@@ -262,7 +272,7 @@ public:
     inline int getMaxIter(void) const { return this->maxIter; }
     void setMaxIter(int maxIter);
 
-    void setGenerator(mnd::Generator* generator);
+    void setGenerator(mnd::MandelGenerator* generator);
 
     void clearCells(void);
 
@@ -282,7 +292,7 @@ class MandelWidget : public QOpenGLWidget
     Q_OBJECT
 private:
     mnd::MandelContext& mndContext;
-    mnd::Generator* generator;
+    mnd::MandelGenerator* generator;
 
     bool smoothColoring = true;
 
@@ -291,6 +301,9 @@ private:
     bool initialized = false;
     int maxIterations = 2000;
 
+    volatile bool selectingPoint = false;
+    float pointX;
+    float pointY;
 
     volatile bool rubberbanding = false;
     QRectF rubberband;
@@ -305,7 +318,7 @@ private:
 
     std::unique_ptr<MandelView> mandelView;
 public:
-    MandelWidget(mnd::MandelContext& ctxt, mnd::Generator* generator, QWidget* parent = nullptr);
+    MandelWidget(mnd::MandelContext& ctxt, mnd::MandelGenerator* generator, QWidget* parent = nullptr);
     ~MandelWidget(void) override;
 
     inline const Gradient& getGradient(void) const { return gradient; }
@@ -320,8 +333,12 @@ public:
     inline int getMaxIterations(void) const { return maxIterations; }
     void setMaxIterations(int maxIter);
 
-    inline mnd::Generator* getGenerator(void) const { return generator; }
-    void setGenerator(mnd::Generator* generator);
+    void setJuliaPos(const mnd::Real& x, const mnd::Real& y);
+    const mnd::Real& getJuliaX(void) { return mandelView->calcer.juliaX; }
+    const mnd::Real& getJuliaY(void) { return mandelView->calcer.juliaY; }
+
+    inline mnd::MandelGenerator* getGenerator(void) const { return generator; }
+    void setGenerator(mnd::MandelGenerator* generator);
 
     void initializeGL(void) override;
     void paintGL() override;
@@ -331,10 +348,12 @@ private:
 
     void drawRubberband(void);
     void drawInfo(void);
+    void drawPoint(void);
 public:
 
     void zoom(float scale, float x = 0.5f, float y = 0.5f);
     void setViewport(const mnd::MandelViewport& viewport);
+    void selectPoint(void);
 
     void requestRecalc(void);
 
@@ -347,5 +366,6 @@ public:
     inline const mnd::MandelViewport& getViewport(void) const { return targetViewport; }
 signals:
     void needsUpdate(const mnd::MandelInfo vp);
+    void pointSelected(mnd::Real x, mnd::Real y);
 };
 

+ 4 - 4
choosegenerators.cpp

@@ -79,7 +79,7 @@ std::pair<long long, std::chrono::nanoseconds> Benchmarker::measureMips(const st
     return std::make_pair(sum, duration_cast<nanoseconds>(after - before));
 }
 
-double Benchmarker::benchmarkResult(mnd::Generator& mg) const
+double Benchmarker::benchmarkResult(mnd::MandelGenerator& mg) const
 {
     size_t testIndex = 0;
 
@@ -244,7 +244,7 @@ void ChooseGenerators::on_buttonBox_accepted()
             QString genString = tableContent.at(i).second->currentText();
 
             mnd::Real precision = mnd::Real(precString.toStdString().c_str());
-            mnd::Generator* generator = generators.at(genString);
+            mnd::MandelGenerator* generator = generators.at(genString);
             if (generator)
                 createdGenerator->addGenerator(precision, *generator);
         }
@@ -260,7 +260,7 @@ void ChooseGenerators::on_run_clicked()
 {
     ui->progressBar->setValue(0);
     for (int i = 0; i < ui->generatorTable->rowCount(); i++) {
-        mnd::Generator* gen = actualGenerators.at(i);
+        mnd::MandelGenerator* gen = actualGenerators.at(i);
         if (gen != nullptr) {
             Benchmarker* bench = new Benchmarker(mndCtxt, *gen, i, 100.0f * (i + 1) / ui->generatorTable->rowCount());
             QObject::connect(bench, &Benchmarker::finished, this, &ChooseGenerators::setBenchmarkResult);
@@ -278,7 +278,7 @@ void ChooseGenerators::on_generatorTable_cellDoubleClicked(int row, int column)
         msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
         int response = msgBox.exec();
         if (response == QMessageBox::Yes) {
-            mnd::Generator* gen = actualGenerators.at(row);
+            mnd::MandelGenerator* gen = actualGenerators.at(row);
             if (gen != nullptr) {
                 ui->progressBar->setValue(0);
                 Benchmarker* bench = new Benchmarker(mndCtxt, *gen, row, 100.0f);

+ 5 - 5
choosegenerators.h

@@ -27,12 +27,12 @@ class Benchmarker : public QObject, public QRunnable
     Q_OBJECT
 private:
     mnd::MandelContext& mndContext;
-    mnd::Generator& generator;
+    mnd::MandelGenerator& generator;
     int row;
     float percentage;
     static const std::vector<mnd::MandelInfo> benches;
 public:
-    inline Benchmarker(mnd::MandelContext& mndContext, mnd::Generator& generator, int row, float percentage) :
+    inline Benchmarker(mnd::MandelContext& mndContext, mnd::MandelGenerator& generator, int row, float percentage) :
         mndContext{ mndContext },
         generator{ generator },
         row{ row },
@@ -45,7 +45,7 @@ public:
     static mnd::MandelViewport benchViewport(void);
 
     std::pair<long long, std::chrono::nanoseconds> measureMips(const std::function<Bitmap<float>*()>& bench) const;
-    double benchmarkResult(mnd::Generator& mg) const;
+    double benchmarkResult(mnd::MandelGenerator& mg) const;
 
     void run(void) override;
 
@@ -61,11 +61,11 @@ private:
     Ui::ChooseGenerators* sadfgsdfg;
     std::unique_ptr<Ui::ChooseGenerators> ui;
     mnd::MandelContext& mndCtxt;
-    std::map<QString, mnd::Generator*> generators;
+    std::map<QString, mnd::MandelGenerator*> generators;
     std::vector<std::pair<QLineEdit*, QComboBox*>> tableContent;
     std::unique_ptr<QValidator> floatValidator;
     std::unique_ptr<mnd::AdaptiveGenerator> createdGenerator;
-    std::vector<mnd::Generator*> actualGenerators;
+    std::vector<mnd::MandelGenerator*> actualGenerators;
     QThreadPool benchmarker;
 public:
     ChooseGenerators(mnd::MandelContext& mndCtxt, QWidget* parent = nullptr);

+ 1 - 0
libmandel/CMakeLists.txt

@@ -35,6 +35,7 @@ SET(MandelSources
     src/MandelUtil.cpp
     src/Types.cpp
     src/OpenClCode.cpp
+    src/JuliaGenerators.cpp
 )
 FILE(GLOB MandelHeaders include/*.h)
 

+ 1 - 1
libmandel/include/ClGenerators.h

@@ -24,7 +24,7 @@ namespace mnd
 }
 
 
-class mnd::ClGenerator : public Generator
+class mnd::ClGenerator : public MandelGenerator
 {
 protected:
     cl::Device device;

+ 24 - 24
libmandel/include/CpuGenerators.h

@@ -21,11 +21,11 @@ namespace mnd
 
 
 template<typename T, mnd::CpuExtension ex, bool parallel>
-class mnd::CpuGenerator : public Generator
+class mnd::CpuGenerator : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<T>() }
+        MandelGenerator{ mnd::getPrecision<T>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -33,11 +33,11 @@ public:
 
 
 template<typename T, bool parallel>
-class mnd::CpuGenerator<T, mnd::NONE, parallel> : public Generator
+class mnd::CpuGenerator<T, mnd::NONE, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<T>() }
+        MandelGenerator{ mnd::getPrecision<T>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -45,22 +45,22 @@ public:
 
 #if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) 
 template<bool parallel>
-class mnd::CpuGenerator<float, mnd::X86_SSE2, parallel> : public Generator
+class mnd::CpuGenerator<float, mnd::X86_SSE2, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<float>() }
+        MandelGenerator{ mnd::getPrecision<float>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
 };
 
 template<bool parallel>
-class mnd::CpuGenerator<double, mnd::X86_SSE2, parallel> : public Generator
+class mnd::CpuGenerator<double, mnd::X86_SSE2, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<double>() }
+        MandelGenerator{ mnd::getPrecision<double>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -68,33 +68,33 @@ public:
 
 
 template<bool parallel>
-class mnd::CpuGenerator<float, mnd::X86_AVX, parallel> : public Generator
+class mnd::CpuGenerator<float, mnd::X86_AVX, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<float>() }
+        MandelGenerator{ mnd::getPrecision<float>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
 };
 
 template<bool parallel>
-class mnd::CpuGenerator<double, mnd::X86_AVX, parallel> : public Generator
+class mnd::CpuGenerator<double, mnd::X86_AVX, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<double>() }
+        MandelGenerator{ mnd::getPrecision<double>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
 };
 
 template<bool parallel>
-class mnd::CpuGenerator<mnd::DoubleDouble, mnd::X86_AVX, parallel> : public Generator
+class mnd::CpuGenerator<mnd::DoubleDouble, mnd::X86_AVX, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<DoubleDouble>() }
+        MandelGenerator{ mnd::getPrecision<DoubleDouble>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -127,11 +127,11 @@ public:
 
 
 template<bool parallel>
-class mnd::CpuGenerator<float, mnd::X86_AVX_FMA, parallel> : public Generator
+class mnd::CpuGenerator<float, mnd::X86_AVX_FMA, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<float>() }
+        MandelGenerator{ mnd::getPrecision<float>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -139,11 +139,11 @@ public:
 
 
 template<bool parallel>
-class mnd::CpuGenerator<double, mnd::X86_AVX_FMA, parallel> : public Generator
+class mnd::CpuGenerator<double, mnd::X86_AVX_FMA, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<double>() }
+        MandelGenerator{ mnd::getPrecision<double>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -151,11 +151,11 @@ public:
 
 
 template<bool parallel>
-class mnd::CpuGenerator<mnd::DoubleDouble, mnd::X86_AVX_FMA, parallel> : public Generator
+class mnd::CpuGenerator<mnd::DoubleDouble, mnd::X86_AVX_FMA, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<DoubleDouble>() }
+        MandelGenerator{ mnd::getPrecision<DoubleDouble>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -163,11 +163,11 @@ public:
 
 
 template<bool parallel>
-class mnd::CpuGenerator<float, mnd::X86_AVX_512, parallel> : public Generator
+class mnd::CpuGenerator<float, mnd::X86_AVX_512, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<float>() }
+        MandelGenerator{ mnd::getPrecision<float>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);
@@ -175,11 +175,11 @@ public:
 
 
 template<bool parallel>
-class mnd::CpuGenerator<double, mnd::X86_AVX_512, parallel> : public Generator
+class mnd::CpuGenerator<double, mnd::X86_AVX_512, parallel> : public MandelGenerator
 {
 public:
     inline CpuGenerator(void) :
-        Generator{ mnd::getPrecision<double>() }
+        MandelGenerator{ mnd::getPrecision<double>() }
     {
     }
     virtual void generate(const MandelInfo& info, float* data);

+ 37 - 14
libmandel/include/Generators.h

@@ -11,7 +11,8 @@
 
 namespace mnd
 {
-    class Generator;
+    class MandelGenerator;
+    class JuliaGenerator;
 
     class AdaptiveGenerator;
 
@@ -47,42 +48,64 @@ namespace mnd
 }
 
 
-class mnd::Generator
+class mnd::MandelGenerator
 {
 protected:
     Real precision;
 public:
-    inline Generator(const Real& precision) :
+    inline MandelGenerator(const Real& precision) :
         precision{ precision }
     {
     }
 
-    virtual ~Generator(void);
+    virtual ~MandelGenerator(void);
 
 
-    Generator(const Generator&) = delete;
-    Generator& operator=(const Generator&) = delete;
+    MandelGenerator(const MandelGenerator&) = delete;
+    MandelGenerator& operator=(const MandelGenerator&) = delete;
 
-    Generator(Generator&&) = default;
-    Generator& operator=(Generator&&) = default;
+    MandelGenerator(MandelGenerator&&) = default;
+    MandelGenerator& operator=(MandelGenerator&&) = default;
 
     virtual void generate(const MandelInfo& info, float* data) = 0;
     virtual Real getPrecision(void) const;
 };
 
 
-class mnd::AdaptiveGenerator : public Generator
+class mnd::JuliaGenerator : public MandelGenerator
 {
-    std::map<Real, Generator*, std::greater<Real>> generators;
+public:
+    inline JuliaGenerator(const Real& precision) :
+        MandelGenerator{ precision }
+    {
+    }
+
+    virtual ~JuliaGenerator(void);
+
+
+    JuliaGenerator(const JuliaGenerator&) = delete;
+    JuliaGenerator& operator=(const JuliaGenerator&) = delete;
+
+    JuliaGenerator(JuliaGenerator&&) = default;
+    JuliaGenerator& operator=(JuliaGenerator&&) = default;
+
+    virtual void generate(const MandelInfo& info, float* data) = 0;
+};
+
+
+
+class mnd::AdaptiveGenerator : public MandelGenerator
+{
+    std::map<Real, MandelGenerator*, std::greater<Real>> generators;
 public:
     AdaptiveGenerator(void);
-    AdaptiveGenerator(Generator* floatGen, Generator* doubleGen);
+    AdaptiveGenerator(MandelGenerator* floatGen, MandelGenerator* doubleGen);
     virtual ~AdaptiveGenerator(void) = default;
 
-    void addGenerator(const Real& precision, Generator& generator);
-    void addGenerator(Precision p, Generator& generator);
+    void addGenerator(const Real& precision, MandelGenerator& generator);
+    void addGenerator(Precision p, MandelGenerator& generator);
     
-    const std::map<Real, Generator*, std::greater<Real>>& getGenerators(void) const { return generators; }
+    const std::map<Real, MandelGenerator*, std::greater<Real>>& getGenerators(void) const { return generators; }
     inline void clear(void) { generators.clear(); }
 
     virtual void generate(const MandelInfo& info, float* data) override;

+ 27 - 0
libmandel/include/JuliaGenerators.h

@@ -0,0 +1,27 @@
+#ifndef MANDEL_JULIAGENERATORS_H
+#define MANDEL_JULIAGENERATORS_H
+
+#include "Generators.h"
+
+
+
+namespace mnd
+{
+    class JuliaGeneratorFloat;
+}
+
+
+class mnd::JuliaGeneratorFloat : public JuliaGenerator
+{
+protected:
+    mnd::Real a;
+    mnd::Real b;
+public:
+    JuliaGeneratorFloat(const mnd::Real& precision);
+    virtual ~JuliaGeneratorFloat(void);
+
+    virtual void generate(const MandelInfo& info, float* data);
+};
+
+#endif // MANDEL_JULIAGENERATORS_H
+

+ 7 - 4
libmandel/include/Mandel.h

@@ -61,7 +61,7 @@ private:
     std::string vendor;
     std::string name;
 
-    std::map<GeneratorType, std::unique_ptr<Generator>> generators;
+    std::map<GeneratorType, std::unique_ptr<MandelGenerator>> mandelGenerators;
 
     MandelDevice(void);
 public:
@@ -73,7 +73,7 @@ public:
     inline const std::string& getVendor(void) const { return vendor; }
     inline const std::string& getName(void) const { return name; }
 
-    Generator* getGenerator(GeneratorType type) const;
+    MandelGenerator* getGenerator(GeneratorType type) const;
 
     std::vector<GeneratorType> getSupportedTypes(void) const;
 };
@@ -86,9 +86,10 @@ private:
 
     CpuInfo cpuInfo;
 
-    std::map<GeneratorType, std::unique_ptr<Generator>> cpuGenerators;
+    std::map<GeneratorType, std::unique_ptr<MandelGenerator>> cpuGenerators;
 
     std::unique_ptr<AdaptiveGenerator> adaptiveGenerator;
+    std::unique_ptr<JuliaGenerator> juliaGenerator;
 
     std::vector<MandelDevice> devices;
 
@@ -105,10 +106,12 @@ public:
     AdaptiveGenerator& getDefaultGenerator(void);
     const std::vector<MandelDevice>& getDevices(void);
 
-    Generator* getCpuGenerator(mnd::GeneratorType type);
+    MandelGenerator* getCpuGenerator(mnd::GeneratorType type);
     std::vector<GeneratorType> getSupportedTypes(void) const;
 
     const CpuInfo& getCpuInfo(void) const { return cpuInfo; }
+
+    JuliaGenerator& getJuliaGenerator(void);
 };
 
 

+ 11 - 1
libmandel/include/MandelUtil.h

@@ -52,10 +52,16 @@ struct mnd::MandelViewport
      */
     static MandelViewport standardView(void);
 
+    /*!
+     * \brief returns a viewport with (0, 0) in the center
+     */
+    static MandelViewport centerView(void);
+
     inline Real right() const { return x + width; }
     inline Real bottom() const { return y + height; }
 };
 
+
 struct mnd::MandelInfo
 {
     /// viewport
@@ -66,12 +72,16 @@ struct mnd::MandelInfo
 
     /// height of the bitmap to be generated
     long bHeight;
-    
+
     /// maximum iterations
     long maxIter;
 
     /// smooth coloring
     bool smooth;
+
+    Real juliaX;
+    Real juliaY;
 };
 
+
 #endif // MANDEL_MANDELUTIL_H

+ 1 - 1
libmandel/src/ClGenerators.cpp

@@ -60,7 +60,7 @@ Device getDevice(Platform& platform, int i, bool display = false) {
 
 
 ClGenerator::ClGenerator(cl::Device device, const mnd::Real& precision) :
-    Generator{ precision },
+    MandelGenerator{ precision },
     device{ device }
 {
     /*Platform p = getPlatform();

+ 14 - 7
libmandel/src/Generators.cpp

@@ -2,27 +2,34 @@
 
 #include <cstdio>
 
-using mnd::Generator;
+using mnd::MandelGenerator;
+using mnd::JuliaGenerator;
 using mnd::AdaptiveGenerator;
 
-Generator::~Generator(void)
+MandelGenerator::~MandelGenerator(void)
 {
 }
 
 
-mnd::Real Generator::getPrecision(void) const
+mnd::Real MandelGenerator::getPrecision(void) const
 {
     return precision;
 }
 
 
+
+JuliaGenerator::~JuliaGenerator(void)
+{
+}
+
+
 AdaptiveGenerator::AdaptiveGenerator(void) :
-    Generator{ 0.0 }
+    MandelGenerator{ 0.0 }
 {
 }
 
 
-AdaptiveGenerator::AdaptiveGenerator(Generator* floatGen, Generator* doubleGen) :
+AdaptiveGenerator::AdaptiveGenerator(MandelGenerator* floatGen, MandelGenerator* doubleGen) :
     AdaptiveGenerator{}
 {
     generators.insert({ 0.0000001, floatGen });
@@ -38,13 +45,13 @@ AdaptiveGenerator::AdaptiveGenerator(Generator* floatGen, Generator* doubleGen,
 }*/
 
 
-void AdaptiveGenerator::addGenerator(const mnd::Real& precision, mnd::Generator& generator)
+void AdaptiveGenerator::addGenerator(const mnd::Real& precision, mnd::MandelGenerator& generator)
 {
     generators.insert({ precision, &generator });
 }
 
 
-void AdaptiveGenerator::addGenerator(mnd::Precision p, Generator& generator)
+void AdaptiveGenerator::addGenerator(mnd::Precision p, MandelGenerator& generator)
 {
     generators.insert({ mnd::getPrecision(p), &generator });
 }

+ 117 - 0
libmandel/src/JuliaGenerators.cpp

@@ -0,0 +1,117 @@
+#include "Generators.h"
+#include "JuliaGenerators.h"
+
+#include <omp.h>
+
+mnd::JuliaGeneratorFloat::JuliaGeneratorFloat(const mnd::Real& precision) :
+    JuliaGenerator{ precision },
+    a{ a },
+    b{ b }
+{
+}
+
+
+mnd::JuliaGeneratorFloat::~JuliaGeneratorFloat(void)
+{
+}
+
+
+void mnd::JuliaGeneratorFloat::generate(const MandelInfo& info, float * data)
+{
+    const MandelViewport& view = info.view;
+
+    using T = double;
+
+
+    const float dppf = float(view.width / info.bWidth);
+    const float viewxf = float(view.x);
+    __m256 viewx = { viewxf, viewxf, viewxf, viewxf, viewxf, viewxf, viewxf, viewxf };
+    __m256 dpp = { dppf, dppf, dppf, dppf, dppf, dppf, dppf, dppf };
+
+
+    T juliaa = mnd::convert<T>(info.juliaX);
+    T juliab = mnd::convert<T>(info.juliaY);
+    __m256 constA = { juliaa, juliaa, juliaa, juliaa, juliaa, juliaa, juliaa,juliaa };
+    __m256 constB = { juliab, juliab, juliab, juliab, juliab, juliab, juliab, juliab};
+
+    omp_set_num_threads(omp_get_num_procs());
+#pragma omp parallel for schedule(static, 1)
+    for (long j = 0; j < info.bHeight; j++) {
+        T y = T(view.y) + T(j) * T(view.height / info.bHeight);
+        __m256 ys = {y, y, y, y, y, y, y, y};
+        long i = 0;
+        for (i; i < info.bWidth; i += 8) {
+            __m256 pixc = { float(i), float(i + 1), float(i + 2), float(i + 3), float(i + 4), float(i + 5), float(i + 6), float(i + 7) };
+
+            __m256 xs = _mm256_add_ps(_mm256_mul_ps(dpp, pixc), viewx);
+
+            __m256 counter = { 0, 0, 0, 0, 0, 0, 0, 0 };
+            __m256 adder = { 1, 1, 1, 1, 1, 1, 1, 1 };
+            __m256 resultsa = { 0, 0, 0, 0, 0, 0, 0, 0 };
+            __m256 resultsb = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+            __m256 threshold = { 16.0f, 16.0f, 16.0f, 16.0f, 16.0f, 16.0f, 16.0f, 16.0f };
+
+            __m256 a = xs;
+            __m256 b = ys;
+            if (info.smooth) {
+                for (int k = 0; k < info.maxIter; k++) {
+                    __m256 aa = _mm256_mul_ps(a, a);
+                    __m256 bb = _mm256_mul_ps(b, b);
+                    __m256 abab = _mm256_mul_ps(a, b); abab = _mm256_add_ps(abab, abab);
+                    a = _mm256_add_ps(_mm256_sub_ps(aa, bb), constA);
+                    b = _mm256_add_ps(abab, constB);
+                    __m256 cmp = _mm256_cmp_ps(_mm256_add_ps(aa, bb), threshold, _CMP_LE_OQ);
+                    resultsa = _mm256_or_ps(_mm256_andnot_ps(cmp, resultsa), _mm256_and_ps(cmp, a));
+                    resultsb = _mm256_or_ps(_mm256_andnot_ps(cmp, resultsb), _mm256_and_ps(cmp, b));
+                    adder = _mm256_and_ps(adder, cmp);
+                    counter = _mm256_add_ps(counter, adder);
+                    if ((k & 0x7) == 0 && _mm256_testz_ps(cmp, cmp) != 0) {
+                        break;
+                    }
+                }
+            }
+            else {
+                for (int k = 0; k < info.maxIter; k++) {
+                    __m256 aa = _mm256_mul_ps(a, a);
+                    __m256 bb = _mm256_mul_ps(b, b);
+                    __m256 abab = _mm256_mul_ps(a, b); abab = _mm256_add_ps(abab, abab);
+                    a = _mm256_add_ps(_mm256_sub_ps(aa, bb), constA);
+                    b = _mm256_add_ps(abab, constB);
+                    __m256 cmp = _mm256_cmp_ps(_mm256_add_ps(aa, bb), threshold, _CMP_LE_OQ);
+                    adder = _mm256_and_ps(adder, cmp);
+                    counter = _mm256_add_ps(counter, adder);
+                    if ((k & 0x7) == 0 && _mm256_testz_ps(cmp, cmp) != 0) {
+                        break;
+                    }
+                }
+            }
+
+
+            auto alignVec = [](float* data) -> float* {
+                void* aligned = data;
+                ::size_t length = 64;
+                std::align(32, 8 * sizeof(float), aligned, length);
+                return static_cast<float*>(aligned);
+            };
+
+            float resData[16];
+            float* ftRes = alignVec(resData);
+            float* resa = (float*) &resultsa;
+            float* resb = (float*) &resultsb;
+
+            _mm256_store_ps(ftRes, counter);
+            for (int k = 0; k < 8 && i + k < info.bWidth; k++) {
+                if (info.smooth) {
+                    data[i + k + j * info.bWidth] = ftRes[k] <= 0 ? info.maxIter :
+                        ftRes[k] >= info.maxIter ? info.maxIter :
+                        ((float)ftRes[k]) + 1 - ::log(::log(resa[k] * resa[k] + resb[k] * resb[k]) / 2) / ::log(2.0f);
+                }
+                else {
+                    data[i + k + j * info.bWidth] = ftRes[k] <= 0 ? info.maxIter : ftRes[k];
+                }
+            }
+        }
+    }
+}
+

+ 22 - 12
libmandel/src/Mandel.cpp

@@ -2,13 +2,14 @@
 #include "Fixed.h"
 
 #include "CpuGenerators.h"
+#include "JuliaGenerators.h"
 #include "ClGenerators.h"
 
 #include <map>
 
 using mnd::MandelDevice;
 using mnd::MandelContext;
-using mnd::Generator;
+using mnd::MandelGenerator;
 using mnd::AdaptiveGenerator;
 
 template<typename T, typename U>
@@ -81,10 +82,10 @@ MandelDevice::MandelDevice(void)
 }
 
 
-mnd::Generator* MandelDevice::getGenerator(mnd::GeneratorType type) const
+mnd::MandelGenerator* MandelDevice::getGenerator(mnd::GeneratorType type) const
 {
-    auto it = generators.find(type);
-    if (it != generators.end())
+    auto it = mandelGenerators.find(type);
+    if (it != mandelGenerators.end())
         return it->second.get();
     else
         return nullptr;
@@ -94,7 +95,7 @@ mnd::Generator* MandelDevice::getGenerator(mnd::GeneratorType type) const
 std::vector<mnd::GeneratorType> MandelDevice::getSupportedTypes(void) const
 {
     std::vector<GeneratorType> types;
-    for (auto& [type, gen] : generators) {
+    for (auto& [type, gen] : mandelGenerators) {
         types.push_back(type);
     }
     return types;
@@ -272,9 +273,9 @@ std::vector<MandelDevice> MandelContext::createDevices(void)
             md.vendor = device.getInfo<CL_DEVICE_VENDOR>();
             //printf("    using opencl device: %s\n", md.name.c_str());
             try {
-                md.generators.insert({ GeneratorType::FLOAT, std::make_unique<ClGeneratorFloat>(device) });
-                md.generators.insert({ GeneratorType::FIXED64, std::make_unique<ClGenerator64>(device) });
-                md.generators.insert({ GeneratorType::DOUBLE_FLOAT, std::make_unique<ClGeneratorDoubleFloat>(device) });
+                md.mandelGenerators.insert({ GeneratorType::FLOAT, std::make_unique<ClGeneratorFloat>(device) });
+                md.mandelGenerators.insert({ GeneratorType::FIXED64, std::make_unique<ClGenerator64>(device) });
+                md.mandelGenerators.insert({ GeneratorType::DOUBLE_FLOAT, std::make_unique<ClGeneratorDoubleFloat>(device) });
             }
             catch (const std::string& err) {
                 printf("err: %s", err.c_str());
@@ -282,9 +283,9 @@ std::vector<MandelDevice> MandelContext::createDevices(void)
 
             if (supportsDouble) {
                 try {
-                    md.generators.insert({ GeneratorType::DOUBLE, std::make_unique<ClGeneratorDouble>(device) });
-                    md.generators.insert({ GeneratorType::DOUBLE_DOUBLE, std::make_unique<ClGeneratorDoubleDouble>(device) });
-                    md.generators.insert({ GeneratorType::QUAD_DOUBLE, std::make_unique<ClGeneratorQuadDouble>(device) });
+                    md.mandelGenerators.insert({ GeneratorType::DOUBLE, std::make_unique<ClGeneratorDouble>(device) });
+                    md.mandelGenerators.insert({ GeneratorType::DOUBLE_DOUBLE, std::make_unique<ClGeneratorDoubleDouble>(device) });
+                    md.mandelGenerators.insert({ GeneratorType::QUAD_DOUBLE, std::make_unique<ClGeneratorQuadDouble>(device) });
                 }
                 catch (const std::string& err) {
                     printf("err: %s", err.c_str());
@@ -320,7 +321,7 @@ const std::vector<MandelDevice>& MandelContext::getDevices(void)
 }
 
 
-Generator* MandelContext::getCpuGenerator(mnd::GeneratorType type)
+MandelGenerator* MandelContext::getCpuGenerator(mnd::GeneratorType type)
 {
     auto it = cpuGenerators.find(type);
     if (it != cpuGenerators.end())
@@ -338,3 +339,12 @@ std::vector<mnd::GeneratorType> MandelContext::getSupportedTypes(void) const
     }
     return types;
 }
+
+
+mnd::JuliaGenerator& MandelContext::getJuliaGenerator()
+{
+    juliaGenerator = std::make_unique<JuliaGeneratorFloat>(getPrecision<double>());
+    return *juliaGenerator;
+}
+
+

+ 17 - 1
libmandel/src/MandelUtil.cpp

@@ -1,6 +1,8 @@
 #include "MandelUtil.h"
 
 using mnd::MandelViewport;
+using mnd::MandelInfo;
+
 
 void MandelViewport::adjustAspectRatio(double nwidth, double nheight)
 {
@@ -44,6 +46,7 @@ void MandelViewport::zoom(float scale, float xz, float yz)
     height *= scale;
 }
 
+
 MandelViewport MandelViewport::standardView(void)
 {
     return MandelViewport{
@@ -52,4 +55,17 @@ MandelViewport MandelViewport::standardView(void)
         3,
         3
     };
-}
+}
+
+
+MandelViewport MandelViewport::centerView(void)
+{
+    return MandelViewport{
+        -2,
+        -2,
+        4,
+        4
+    };
+}
+
+