--- /dev/null
+#ifdef ECERE_STATIC
+import static "ecere"
+#else
+import "ecere"
+#endif
+
+struct Complex { double a, b; };
+
+void drawMandelbrot(Bitmap bmp, float range, Complex center, ColorAlpha * palette, int nPalEntries, int nIterations, float scale)
+{
+ int x, y;
+ int w = bmp.width, h = bmp.height;
+ ColorAlpha * picture = (ColorAlpha *)bmp.picture;
+ double logOf2 = log(2), logOfPt5 = log(0.5);
+ Complex d
+ {
+ w > h ? range : range * w / h,
+ h > w ? range : range * h / w
+ };
+ Complex C0 { center.a - d.a/2, center.b - d.b/2 };
+ Complex C = C0;
+ double delta = d.a / w;
+
+ for(y = 0; y < h; y++, C.a = C0.a, C.b += delta)
+ {
+ for(x = 0; x < w; x++, picture++, C.a += delta)
+ {
+ Complex Z { };
+ int i;
+ double ii = 0;
+ bool out = false;
+ double Za2 = Z.a * Z.a, Zb2 = Z.b * Z.b;
+ for(i = 0; i < nIterations; i++)
+ {
+ double z2;
+ Z = { Za2 - Zb2, 2*Z.a*Z.b };
+ Z.a += C.a;
+ Z.b += C.b;
+ Za2 = Z.a * Z.a, Zb2 = Z.b * Z.b;
+ z2 = Za2 + Zb2;
+
+ if(z2 >= 2*2)
+ {
+ ii = (double)(i + 1 - logOfPt5 - log(log(z2)) / logOf2);
+ out = true;
+ break;
+ }
+ }
+ if(out)
+ {
+ float si = (float)(ii * scale);
+ int i0 = ((int)si) % nPalEntries;
+ *picture = palette[i0];
+ }
+ else
+ *picture = black;
+ }
+ }
+}
--- /dev/null
+{
+ "Version" : 0.2,
+ "ModuleName" : "mandelbrot",
+ "Options" : {
+ "Warnings" : "All",
+ "TargetType" : "Executable",
+ "TargetFileName" : "mandelbrot",
+ "Libraries" : [
+ "ecere"
+ ]
+ },
+ "Configurations" : [
+ {
+ "Name" : "Debug",
+ "Options" : {
+ "Debug" : true,
+ "Optimization" : "Speed",
+ "PreprocessorDefinitions" : [
+ "_DEBUG"
+ ],
+ "CompilerOptions" : [
+ "-mmmx",
+ "-msse",
+ "-msse2",
+ "-msse3",
+ "-msse4"
+ ],
+ "FastMath" : true
+ }
+ },
+ {
+ "Name" : "Release",
+ "Options" : {
+ "Debug" : false,
+ "Optimization" : "Speed",
+ "FastMath" : true
+ }
+ },
+ {
+ "Name" : "Emscripten",
+ "Options" : {
+ "PreprocessorDefinitions" : [
+ "ECERE_STATIC"
+ ],
+ "TargetFileName" : "mandelbrot.html",
+ "Libraries" : [
+ "ecereStatic",
+ "z",
+ "jpeg",
+ "png",
+ "freetype"
+ ],
+ "LibraryDirs" : [
+ "../../../ecere/obj/emscripten.linux.emscripten"
+ ],
+ "FastMath" : true
+ }
+ }
+ ],
+ "Files" : [
+ "mandelbrot.ec",
+ "mandelbrotUI.ec"
+ ],
+ "ResourcesPath" : "",
+ "Resources" : [
+ {
+ "Folder" : "ecere",
+ "Files" : [
+ {
+ "Folder" : "shaders",
+ "Files" : [
+ "../../../ecere/src/gfx/drivers/gl3/fixed.frag",
+ "../../../ecere/src/gfx/drivers/gl3/fixed.vertex"
+ ]
+ }
+ ],
+ "Options" : {
+ "ExcludeFromBuild" : true
+ },
+ "Configurations" : [
+ {
+ "Name" : "Emscripten",
+ "Options" : {
+ "ExcludeFromBuild" : false
+ }
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+import "mandelbrot"
+
+class Mandelbrot : Window
+{
+ caption = $"Mandelbrot";
+#if defined(__EMSCRIPTEN__)
+ anchor = { 0,0,0,0 };
+#else
+ borderStyle = sizable;
+ hasMaximize = true;
+ hasMinimize = true;
+ hasClose = true;
+ clientSize = { 600, 600 };
+#endif
+
+ Point mouseStart, mouseEnd;
+ bool dragging;
+ bool needUpdate;
+
+ float scale;
+ int nIterations; nIterations = 256;
+ ColorAlpha * palette;
+ int nPalEntries;
+ Complex center { -0.75, 0 };
+
+ float range; range = 4;
+ Bitmap bmp { };
+
+ Mandelbrot()
+ {
+ static ColorKey keys[] =
+ {
+ { navy, 0.0f },
+ { Color { 146, 213, 237 }, 0.198606268f },
+ { white, 0.3f },
+ { Color { 255, 255, 124 }, 0.444250882f },
+ { Color { 255, 100, 0 }, 0.634146333f },
+ { navy, 1 }
+ };
+
+ nPalEntries = 30000;
+ palette = new ColorAlpha[nPalEntries];
+ scale = nPalEntries / 175.0f;
+ PaletteGradient(palette, nPalEntries, keys, sizeof(keys)/sizeof(keys[0]), 1.0);
+ needUpdate = true;
+ }
+
+ ~Mandelbrot() { delete palette; }
+
+ void OnRedraw(Surface surface)
+ {
+ if(needUpdate)
+ {
+#if defined(__EMSCRIPTEN__)
+ bmp.Free();
+ bmp.Allocate(null, clientSize.w, clientSize.h, 0, pixelFormat888, false);
+#endif
+ drawMandelbrot(bmp, range, center, palette, nPalEntries, nIterations, scale);
+#if defined(__EMSCRIPTEN__)
+ bmp.MakeDD(displaySystem);
+#endif
+ needUpdate = false;
+ }
+ surface.Blit(bmp, 0,0, 0,0, bmp.width, bmp.height);
+
+ if(dragging)
+ {
+ surface.foreground = lime;
+ surface.Rectangle(mouseStart.x, mouseStart.y, mouseEnd.x, mouseEnd.y);
+ }
+ }
+
+ bool OnLeftButtonDown(int x, int y, Modifiers mods)
+ {
+ mouseEnd = mouseStart = { x, y };
+ Capture();
+ dragging = true;
+ Update(null);
+ return true;
+ }
+
+ bool OnLeftButtonUp(int x, int y, Modifiers mods)
+ {
+ if(dragging)
+ {
+ int dx = Abs(mouseEnd.x - mouseStart.x), dy = Abs(mouseEnd.y - mouseStart.y);
+ if(dx > 4 && dy > 4)
+ {
+ int w = clientSize.w, h = clientSize.h;
+ float rangeX = w > h ? range : range * w / h;
+ float rangeY = h > w ? range : range * h / w;
+
+ center.a += ((mouseStart.x + mouseEnd.x) - w) / 2.0f * rangeX / w;
+ center.b += ((mouseStart.y + mouseEnd.y) - h) / 2.0f * rangeY / h;
+
+ range = dy > dx ? dy * range / h : dx * range / w;
+
+ needUpdate = true;
+ Update(null);
+ }
+ ReleaseCapture();
+ dragging = false;
+ }
+ return true;
+ }
+
+ bool OnMouseMove(int x, int y, Modifiers mods)
+ {
+ if(dragging)
+ {
+ mouseEnd = { x, y };
+ Update(null);
+ }
+ return true;
+ }
+
+ bool OnRightButtonDown(int x, int y, Modifiers mods)
+ {
+ range = 4;
+ nIterations = 256;
+ center = { -0.75, 0 };
+ needUpdate = true;
+ Update(null);
+ return true;
+ }
+
+ void OnResize(int width, int height)
+ {
+ bmp.Allocate(null, width, height, 0, pixelFormat888, false);
+ needUpdate = true;
+ Update(null);
+ }
+
+ bool OnKeyHit(Key key, unichar ch)
+ {
+ switch(key)
+ {
+ case space: case keyPadPlus: case plus:
+ nIterations += 256;
+ needUpdate = true;
+ Update(null);
+ break;
+ }
+ return true;
+ }
+}
+
+Mandelbrot mandelbrotForm {};