ultimatepp/examples/Mandelbrot/main.cpp
İsmail Yılmaz 24943454a9
examples: A simple Mandelbrot fractal viewer, using SIMD extensions (#386)
* examples: A simple Mandelbrot fractal viewer, using SIMD extensions

Examples/Mandelbrot: Cosmetics

examples/Mandelbrot: fix.

* examples/Mandelbrot: Cleanup
2026-06-10 09:44:02 +02:00

175 lines
No EOL
4 KiB
C++

#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();
}