mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-30 06:12:22 -06:00
* examples: A simple Mandelbrot fractal viewer, using SIMD extensions Examples/Mandelbrot: Cosmetics examples/Mandelbrot: fix. * examples/Mandelbrot: Cleanup
175 lines
No EOL
4 KiB
C++
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();
|
|
} |