development version (master branch)
Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
UppHub
Status & Roadmap
FAQ
Authors & License
Forums
Funding U++
Search on this site









SourceForge.net Logo

SourceForge.net Logo

GitHub Logo

Discord Logo

Mandelbrot

 

A simple Mandelbrot fractal viewer, using SIMD extensions when available

 

 

Mandelbrot.h

 

#ifndef _Mandelbrot_Mandelbrot_h

#define _Mandelbrot_Mandelbrot_h

 

// Run this example in release mode (-O3)

 

#include <CtrlLib/CtrlLib.h>

 

using namespace Upp;

 

#define LAYOUTFILE <Mandelbrot/Mandelbrot.lay>

#include <CtrlCore/lay.h>

 

class Mandelbrot : public WithMainLayout<TopWindow> {

public:

    Mandelbrot();

    void Reset();

    void LeftDown(Point pt, dword keyflags) override;

    void LeftUp(Point pt, dword keyflags) override;

    void MouseMove(Point pt, dword keyflags) override;

    void MouseWheel(Point pt, int zdelta, dword keyflags) override;

    

private:

    Image  DrawScalar(int itermax);

    Image  DrawSIMD(int itermax);

    void   Update();

    Rectf  crect = { -2.0f, -1.5f, 1.0f, 1.5f };

    bool   rendering = false;

    Point  panpos = { -1, -1 };

};

 

#endif

 

 

 

main.cpp

 

#include "Mandelbrot.h"

 

// Run this example in release mode (-O3)

 

force_inline RGBA GetFractalColor(int iter, int maxiter)

{

    float hue = (float) iter / maxiter;

    float val = (iter < maxiter) ? 1.0f : 0.0f;

    return HsvColorf(hue, 1.0f, val);

}

 

Mandelbrot::Mandelbrot()

{

    CtrlLayout(*this, "Mandelbrot Viewer");

#ifndef CPU_SIMD

    simd.Hide();

#endif

    for(int i : { 250, 500, 1000 })

        maxiter.Add(i);

    maxiter.SetIndex(1);

    auto cb = [this] { Update(); };

    render << [this] { Reset(); Update(); };

    maxiter << cb;

    simd << cb;

    render.WhenAction();

    view.IgnoreMouse();

}

 

void Mandelbrot::Reset()

{

    rendering = true;

    crect = { -2.0f, -1.5f, 1.0f, 1.5f };

    maxiter.SetIndex(1);

    simd = true;

    rendering = false;

}

 

void Mandelbrot::Update()

{

    if(rendering)

        return;

 

    rendering = true;

    TimeStop ts;

    Image img = ~simd ?    DrawSIMD(~maxiter)  : DrawScalar(~maxiter);

    timing.SetText(Format("Generated in %02.0f milliseconds...", ts.Elapsed() / 1000.0));

    view.SetImage(img);

    rendering = false;

}

 

Image Mandelbrot::DrawScalar(int itermax)

{

    const Size sz = view.GetSize();

    const float dx = crect.GetWidth() / sz.cx;

    const float dy = crect.GetHeight() / sz.cy;

    ImageBuffer ib(sz);

    RGBA* pix = ib;

 

    for(int py = 0; py < sz.cy; py++) {

        const float cy = crect.top + py * dy;

        for(int px = 0; px < sz.cx; px++) {

            float cx = crect.left + px * dx;

            float zx = 0.0f, zy = 0.0f;

            int iter = 0;

            for(int k = 0; k < itermax; k++) {

                float zx2 = zx * zx;

                float zy2 = zy * zy;

                if(zx2 + zy2 >= 4.0f)

                    break;

                float zxy = zx * zy;

                zx = zx2 - zy2 + cx;

                zy = zxy + zxy + cy;

                iter++;

            }

            pix[py * sz.cx + px] = GetFractalColor(iter, itermax);

        }

        ProcessEvents();

    }

 

    return ib;

}

 

Image Mandelbrot::DrawSIMD(int itermax)

{

#ifdef CPU_SIMD

    const Size sz = view.GetSize();

    const float dx = crect.GetWidth() / sz.cx;

    const float dy = crect.GetHeight() / sz.cy;

    ImageBuffer ib(sz);

    RGBA* pix = ib;

    const int block = 4;

 

    const float offsets[block] = { 0.0f, dx, 2.0f * dx, 3.0f * dx };

    f32x4 xoffsets; xoffsets.Load(offsets);

 

    for(int py = 0; py < sz.cy; py++) {

        f32x4 cy4 = f32all(crect.top + py * dy);

        for(int px = 0; px < sz.cx; px += block) {

            f32x4 cx4 = f32all(crect.left + px * dx) + xoffsets;

            f32x4 zx = f32all(0.0f), zy = f32all(0.0f);

            i32x4 iter = i32all(0);

            for(int k = 0; k < itermax; k++) {

                f32x4 zx2 = zx * zx;

                f32x4 zy2 = zy * zy;

                f32x4 mag2 = zx2 + zy2;

                f32x4 mask = mag2 < f32all(4.0f);

                if(!AnyTrue(mask))

                    break;

                f32x4 zxy = zx * zy;

                zx = zx2 - zy2 + cx4;

                zy = zxy + zxy + cy4;

                iter = IncrementIf(iter, mask);

            }

            alignas(16) int tmp[block];

            iter.Store(tmp);

            for(int i = 0; i < block; i++) {

                if(px + i >= sz.cx) break;

                pix[py * sz.cx + px + i] = GetFractalColor(tmp[i], itermax);

            }

        }

        ProcessEvents();

    }

 

    return ib;

#else

    return DrawScalar(itermax);

#endif

}

 

void Mandelbrot::LeftDown(Point pt, dword keyflags)

{

    if(!rendering) {

        panpos = pt;

        SetCapture();

    }

}

 

void Mandelbrot::LeftUp(Point pt, dword keyflags)

{

    if(panpos.x != -1) {

        panpos = Point(-1, -1);

        ReleaseCapture();

    }

}

 

void Mandelbrot::MouseMove(Point pt, dword keyflags)

{

    if(rendering || panpos.x == -1)

        return;

    

    const Size sz = view.GetSize();

    const Sizef csz = crect.GetSize();

   const double dx = (double)(pt.x - panpos.x) / sz.cx * csz.cx;

   const double dy = (double)(pt.y - panpos.y) / sz.cy * csz.cy;

   crect.Offset((float) -dx, (float) -dy);

    panpos = pt;

    Update();

}

 

void Mandelbrot::MouseWheel(Point pt, int zdelta, dword keyflags)

{

    if(rendering)

        return;

 

    const Size sz = view.GetSize();

    const double scale = (zdelta > 0) ? 0.8 : 1.25;

    const Sizef csz = crect.GetSize();

    crect = Rectf(crect.TopLeft() + (Pointf(pt) / Sizef(sz)) * csz * (1.0 - scale), csz * scale);

    Update();

}

 

GUI_APP_MAIN

{

    Mandelbrot().Run();

}

 

 

Mandelbrot.lay

 

LAYOUT(MainLayout, 584, 372)

    ITEM(Upp::ImageCtrl, view, HSizePosZ(4, 4).VSizePosZ(4, 60))

    ITEM(Upp::Button, render, SetLabel(t_("Reset")).RightPosZ(4, 148).BottomPosZ(5, 23))

    ITEM(Upp::Option, simd, SetLabel(t_("Use SIMD")).LeftPosZ(264, 164).BottomPosZ(8, 16))

    ITEM(Upp::StaticText, timing, SetAlign(Upp::ALIGN_CENTER).SetFrame(Upp::ThinInsetFrame()).HSizePosZ(4, 4).BottomPosZ(37, 19))

    ITEM(Upp::DropList, maxiter, LeftPosZ(96, 160).BottomPosZ(6, 19))

    ITEM(Upp::Label, dv___5, SetLabel(t_("Max. iterations")).LeftPosZ(4, 88).BottomPosZ(8, 16))

END_LAYOUT

 

 

 

 

 

Do you want to contribute?