• October 20, 2021, 02:46:57 AM

Author Topic:  Explore fractals (inflection tool)  (Read 5547 times)

0 Members and 1 Guest are viewing this topic.

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Explore fractals (inflection tool)
« on: January 30, 2018, 01:20:46 AM »


Latest version here:
Fractalforums downloads section: https://fractalforums.org/index.php?action=downloads;sa=view;down=21
or: forum post with mega link: https://fractalforums.org/other/55/explore-fractals-inflection-tool/777/msg27981#msg27981


With this program you can test the effect of inflections / Julia morphings on the Mandelbrot set with powers 2, 3, 4 and 5. This works by transforming the plane and then actually rendering the fractal. Each click adds an inflection at the location of the cursor. You can also work with Julia sets. You can go somewhere in the M-set, then use "Toggle Julia" which uses the center of the screen as the seed for the Julia set.

GitHub page: https://github.com/DinkydauSet/ExploreFractals


This is the original post about the very first version:

There are some other features that don't work. The program is in an unfinished state, but Julia morphing works. The precision is limited to that of the double datatype (a little less than 64 bits). It's also slow, compared to fractal extreme, for example. I don't know how to improve the speed.

Download (don't download this bad old version anymore - download the latest version)
Explore_fractals.exe   2.2 MB
https://mega.nz/#!BhdBWSKJ!v6Pj86W8oNSktFPWF44dsJvLscZyOt8LZUkqoxDIxxg

Source code (C++):
Code: [Select]
#include <windows.h>
#include <Windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <tchar.h>
#include <complex.h>
#include <math.h>
//#include <omp.h>
#include <time.h>
#include <chrono>
#include <thread>

using namespace std;
 
// Global variables 
int width = 1200;
int height = 1200;
const int MAX_NUMBER_OF_INFLECTIONS = 500;
bool benchmark = false;
unsigned NUMBER_OF_THREADS;
bool firstPaint = true;

// The main window class name. 
static TCHAR szWindowClass[] = _T("win32app"); 
static TCHAR CLASS_NAME2[]  = _T("Options Window");

// The string that appears in the application's title bar. 
static TCHAR szTitle[] = _T("Explore fractals"); 
static TCHAR TITLE_NAME2[]  = _T("Options");

HINSTANCE hInst;
HWND hOptions;

// Forward declarations of functions included in this code module: 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK OptionsProc(HWND, UINT, WPARAM, LPARAM);

int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int    nCmdShow
)
{
setbuf(stdout, NULL);

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style   = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance   = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* Load a standard icon */
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName   = NULL;
wcex.lpszClassName  = szWindowClass;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); /* use the name "A" to use the project icon */

if (!RegisterClassEx(&wcex)) {
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}

hInst = hInstance;

HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
width+20, height+70,
NULL,
NULL,
hInstance,
NULL
);

if (!hWnd) {
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}

// The parameters to ShowWindow explained:
// hWnd: the value returned from CreateWindow
// nCmdShow: the fourth parameter from WinMain
ShowWindow(hWnd,
nCmdShow);
UpdateWindow(hWnd);

WNDCLASSEX optionsEx;
optionsEx.cbSize = sizeof(WNDCLASSEX);
optionsEx.style   = CS_HREDRAW | CS_VREDRAW;
optionsEx.lpfnWndProc = OptionsProc;
optionsEx.cbClsExtra = 0;
optionsEx.cbWndExtra = 0;
optionsEx.hInstance   = hInstance;
optionsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* Load a standard icon */
optionsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
optionsEx.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
optionsEx.lpszMenuName   = NULL;
optionsEx.lpszClassName  = CLASS_NAME2;
optionsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); /* use the name "A" to use the project icon */

if (!RegisterClassEx(&optionsEx)) {
cout << "error: " << GetLastError() << endl;
MessageBox(NULL,
_T("Call to RegisterClassEx failed for optionsEx!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}

hOptions = CreateWindowEx(0, CLASS_NAME2, TITLE_NAME2, WS_OVERLAPPEDWINDOW, 100, 100, 100, 100, hWnd, NULL, hInst, NULL);
//copy of line //HWND hOptions = CreateWindowEx(0, CLASS_NAME2, TITLE_NAME2, WS_OVERLAPPEDWINDOW, 100, 100, 100, 100, hWnd, NULL, hInst, NULL);

if (!hOptions) {
MessageBox(NULL,
_T("Call to CreateWindow failed for hOptions!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}

ShowWindow(hOptions, nCmdShow);
UpdateWindow(hOptions);

// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return (int) msg.wParam;
}

const int QUIT = 1;
const int RESET = 2;
const int TOGGLE_JULIA = 3;
const int FORMULA_M_2 = 4;
const int FORMULA_BURNING_SHIP = 5;
const int FORMULA_M_3 = 6;
const int FORMULA_M_4 = 7;
const int FORMULA_M_5 = 8;
const int VIEW_GUESSED_PIXELS = 9;
const int WINDOW_OPTIONS = 10;

void AddMenus(HWND hwnd) {

HMENU hMenubar;
HMENU hMenu;

hMenubar = CreateMenu();
hMenu = CreateMenu();
//hMenu, item type, message (number), button text
AppendMenuW(hMenu, MF_STRING, RESET, L"&Reset");
AppendMenuW(hMenu, MF_STRING, TOGGLE_JULIA, L"&Toggle Julia");
AppendMenuW(hMenu, MF_STRING, VIEW_GUESSED_PIXELS, L"&View guessed pixels");
AppendMenuW(hMenu, MF_STRING, WINDOW_OPTIONS, L"&Options");
AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
AppendMenuW(hMenu, MF_STRING, FORMULA_M_2, L"&Mandelbrot");
AppendMenuW(hMenu, MF_STRING, FORMULA_BURNING_SHIP, L"&Burning Ship");
AppendMenuW(hMenu, MF_STRING, FORMULA_M_3, L"&Mandelbrot power 3");
AppendMenuW(hMenu, MF_STRING, FORMULA_M_4, L"Mandelbrot power 4&");
AppendMenuW(hMenu, MF_STRING, FORMULA_M_5, L"Mandelbrot power 5&");
AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
AppendMenuW(hMenu, MF_STRING, QUIT, L"&Quit");

AppendMenuW(hMenubar, MF_POPUP, (UINT_PTR) hMenu, L"&Options");
SetMenu(hwnd, hMenubar);
}

//Fractal global variables
const int CALC_IN_MINIBROT = -2147483647;
const int GUESSED_IN_MINIBROT = -2147483646;
double _Complex center;
double x_range;
double y_range;
int maxIters;
double _Complex juliaSeed = -0.75 + 0.1*I;
bool julia = false;
double rotation = 0;
HBITMAP screenBMP;
UINT * ptPixels;
UINT * ptPixelsTMP = (UINT*)calloc(width*height, 4);
int * fractalCanvas = (int*)calloc(width*height, 4);
int guessingViewType = 0;
int lastRenderID = 0;

double _Complex map(int i, int j) {
return ( creal(center) - x_range/2 + i*(x_range/width) ) + ( cimag(center) - y_range/2 + j*(y_range/height) ) * I;
}

int formulaType = FORMULA_M_2;
double _Complex formula(double _Complex z, double _Complex c) {
switch (formulaType) {
case FORMULA_M_2:
return z*z + c;
case FORMULA_M_3:
return z*z*z + c;
case FORMULA_M_4:
return z*z*z*z + c;
case FORMULA_M_5:
return z*z*z*z*z + c;
case FORMULA_BURNING_SHIP:
return cpow((cabs(creal(z))+cabs(cimag(z))*I), 2) + c;
}
}

int transformation_type = 0;
double _Complex transformation(double _Complex c) {
switch (transformation_type) {
case 0:
return c;
case 1: {
double _Complex z = 0;
for (int i=0; i<5; i++) {
z = z*z + c;
}
return z;
}
case 2:
return ccos(c);
case 3:
return c + 2+2*I;
case 4:
return csqrt(c);
case 5:
return c*c;
}
}

double _Complex* inflectionCoords = (double _Complex*)malloc(MAX_NUMBER_OF_INFLECTIONS*sizeof(double _Complex));
int inflectionCount = 0;
int inflectionPower = 2;
double _Complex inflections(double _Complex c) {
for (int i=inflectionCount-1; i>=0; i--) {
c = cpow(c, inflectionPower) + inflectionCoords[i];
}
return c;
}

void reset() {

//testlocation

/*
x_range = 0.001953125;
y_range = x_range*((double)height/(double)width);
maxIters = 500;
center = -1.372268 + 0.084797*I;
inflectionCount = 0;
*/

//benchmark

/*
x_range = 0.000000476837158203125;
y_range = x_range*((double)height/(double)width);
maxIters = 5000;
center = -0.1051904975 + 0.9269252546*I;
inflectionCount = 0;
*/

/*
//glitch:
x_range = 0.001953;
y_range = x_range*((double)height/(double)width);
maxIters = 5000;
center = -1.768518 + 0.000348*I;
inflectionCount = 0;
*/

///normal settings


x_range = 4;
y_range = x_range*((double)height/(double)width);
maxIters = 4600;
center = 0 + 0*I;


//inflectionCount = 0;

}

unsigned char gradientMod(int iterationCount) {
//printf("gradient entered\n");
iterationCount = (iterationCount*20)%512;
if (iterationCount < 256) {
return iterationCount;
}
else {
return 512 - iterationCount - 1;
}
}

UINT gradient(int iterationCount) {
return RGB(gradientMod(iterationCount+4), gradientMod(iterationCount+2), gradientMod(iterationCount+3));
}

BOOL DrawBitmap (HDC hDC, int x, int y, HBITMAP hBitmap, DWORD dwROP) {
HDC    hDCBits;
BITMAP Bitmap;
BOOL   bResult;
if (!hDC || !hBitmap)
return FALSE;
hDCBits = CreateCompatibleDC(hDC);
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
SelectObject(hDCBits, hBitmap);
bResult = BitBlt(hDC, x, y, Bitmap.bmWidth, Bitmap.bmHeight, hDCBits, 0, 0, dwROP);
DeleteDC(hDCBits);
return bResult;
}

void loadFractalCanvas(bool trueColor) {
//Negative value means guessed pixel. CALC_IN_MINIBROT and GUESSED_IN_MINIBROT are special values.
int iterationCount;
if (trueColor) {
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
iterationCount = fractalCanvas[i*height + j];
if (iterationCount == CALC_IN_MINIBROT) {
ptPixels[width*(height-j-1) + i] = RGB(255, 0, 0);
}
else if (iterationCount == GUESSED_IN_MINIBROT) {
ptPixels[width*(height-j-1) + i] = RGB(0, 0, 255);
}
else if (iterationCount < 0) {
ptPixels[width*(height-j-1) + i] = RGB(0, 255, 0);
}
else {
ptPixels[width*(height-j-1) + i] = gradient(iterationCount);
}
}
}
}
else {
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
iterationCount = fractalCanvas[i*height + j];
if (iterationCount == CALC_IN_MINIBROT) {
ptPixels[width*(height-j-1) + i] = RGB(0, 0, 0);
}
else if (iterationCount == GUESSED_IN_MINIBROT) {
ptPixels[width*(height-j-1) + i] = RGB(0, 0, 0);
}
else if (iterationCount < 0) {
ptPixels[width*(height-j-1) + i] = gradient(-iterationCount);
}
else {
ptPixels[width*(height-j-1) + i] = gradient(iterationCount);
}
}
}
}
}

class Render {
private:
int renderID;
HWND hWnd;
PAINTSTRUCT ps;
HDC hdc;
int threadCount;
public:
int guessedPixelCount;
int pixelGroupings;
Render(int,HWND);
int mandelIterate(int,int);
void renderSilverRect(int,int,int,int,bool,int,bool,int,bool,int,bool,int);
void renderSilverFull();
void start();
};

Render::Render(int currentRenderID, HWND windowHandle) {
renderID = currentRenderID;
hWnd = windowHandle;
}

int Render::mandelIterate(int i, int j) {
int iterationCount = 0;
if (formulaType == FORMULA_M_2) {
double _Complex c = inflections(transformation(map(i, j)));
double cr;
double ci;
double zr;
double zi;
double zrsqr;
double zisqr;
if (julia) {
cr = creal(juliaSeed);
ci = cimag(juliaSeed);
zr = creal(c);
zi = cimag(c);
zrsqr = zr*zr;
zisqr = zi*zi;
}
else {
cr = creal(c);
ci = cimag(c);
zr = 0;
zi = 0;
zrsqr = 0;
zisqr = 0;
}
while (zrsqr + zisqr <= 4.0 && iterationCount < maxIters) {
zi = zr*zi;
zi += zi;
zi += ci;
zr = zrsqr - zisqr + cr;
zrsqr = zr*zr;
zisqr = zi*zi;
iterationCount++;
}
}
else {
if(i%150 == 0 && j%150 == 0) {
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
}
double _Complex c;
double _Complex z;
if (julia) {
c = juliaSeed;
z = inflections(transformation(map(i, j)));
}
else {
c = inflections(transformation(map(i, j)));
z = 0;
}
while (creal(z)*creal(z) + cimag(z)*cimag(z) < 4 && iterationCount < maxIters) {
z = formula(z, c);
iterationCount++;
}
}
if (iterationCount >= maxIters) {
ptPixels[width*(height-j-1) + i] = RGB(0, 0, 0);
iterationCount = CALC_IN_MINIBROT;
fractalCanvas[i*height + j] = CALC_IN_MINIBROT;
}
else {
ptPixels[width*(height-j-1) + i] = gradient(iterationCount);
fractalCanvas[i*height + j] = iterationCount;
}
//cout << "i,j: " << i << "," << j << "  iterationCount:" << iterationCount << endl;
return iterationCount;
}

void Render::renderSilverRect(int imin, int imax, int jmin, int jmax,
bool sameTop, int iterTop, bool sameBottom, int iterBottom, bool sameLeft, int iterLeft, bool sameRight, int iterRight)
{
if (renderID != lastRenderID) {
printf("Render cancelled; terminating thread\n");
return;
}
if (sameRight && sameLeft && sameTop && sameBottom && iterRight == iterTop && iterTop == iterLeft && iterLeft == iterBottom && iterRight != 1 && iterRight != 0) {
//Fill with guessed pixels
for (int i=imin+1; i<imax; i++) {
for (int j=jmin+1; j<jmax; j++) {
if (iterLeft == CALC_IN_MINIBROT || iterLeft >= maxIters) {
ptPixels[width*(height-j-1) + i] = RGB(0, 0, 0);
fractalCanvas[i*height + j] = GUESSED_IN_MINIBROT;
}
else {
ptPixels[width*(height-j-1) + i] = gradient(iterLeft);
fractalCanvas[i*height + j] = -iterLeft;
}
if(i%150 == 0 && j%150 == 0) {
if (benchmark) {
loadFractalCanvas(false);
}
    DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
}
}
}
guessedPixelCount += (imax-imin-1)*(jmax-jmin-2);
return;
}
if (imin >= imax-4 || jmin >= jmax-4) {
//The tile is now very small. Stop the recursion and iterate all pixels.
for (int i=imin; i<=imax; i++) {
for (int j=jmin; j<=jmax; j++) {
mandelIterate(i, j);
if(i%150 == 0 && j%150 == 0) {
if (benchmark) {
loadFractalCanvas(false);
}
    DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
    }
}
}
return;
}
if (imax-imin < jmax-jmin) {
bool sameNewLine = true;
int j = jmin+(jmax-jmin)/2;
//compute new line
int iterNewLine = mandelIterate(imin+1, j);
for (int k=imin+2; k<imax; k++) {
if (mandelIterate(k, j) != iterNewLine) {
sameNewLine = false;
}
}
//check right and left for equality
bool sameRightTop = true;
bool sameLeftTop = true;
bool sameLeftBottom = true;
bool sameRightBottom = true;
int iterRightTop = fractalCanvas[imax*height+jmin];
int iterRightBottom = fractalCanvas[imax*height + j];
int iterLeftTop = fractalCanvas[imin*height+jmin];
int iterLeftBottom = fractalCanvas[imin*height + j];
if (!sameRight) {
for (int k=jmin+1; k<=j; k++) {
if (fractalCanvas[imax*height + k] != iterRightTop) {
sameRightTop = false;
break;
}
}
for (int k=j+1; k<=jmax; k++) {
if (fractalCanvas[imax*height + k] != iterRightBottom) {
sameRightBottom = false;
break;
}
}
}
if (!sameLeft) {
for (int k=jmin+1; k<=j; k++) {
if (fractalCanvas[imin*height + k] != iterLeftTop) {
sameLeftTop = false;
break;
}
}
for (int k=j+1; k<=jmax; k++) {
if (fractalCanvas[imin*height + k] != iterLeftBottom) {
sameLeftBottom = false;
break;
}
}
}
if (threadCount < NUMBER_OF_THREADS) {
thread t1(&Render::renderSilverRect, this, imin, imax, jmin, j,
   sameTop, iterTop, sameNewLine, iterNewLine, sameLeftTop, iterLeftTop, sameRightTop, iterRightTop);
thread t2(&Render::renderSilverRect, this, imin, imax, j, jmax,
   sameNewLine, iterNewLine, sameBottom, iterBottom, sameLeftBottom, iterLeftBottom, sameRightBottom, iterRightBottom);
threadCount += 2;
t1.join(); threadCount--;
t2.join(); threadCount--;
}
else {
if (height-jmax < 0.5*height) {
renderSilverRect(imin, imax, jmin, j,
   sameTop, iterTop, sameNewLine, iterNewLine, sameLeftTop, iterLeftTop, sameRightTop, iterRightTop);
renderSilverRect(imin, imax, j, jmax,
   sameNewLine, iterNewLine, sameBottom, iterBottom, sameLeftBottom, iterLeftBottom, sameRightBottom, iterRightBottom);
}
else {
renderSilverRect(imin, imax, j, jmax,
   sameNewLine, iterNewLine, sameBottom, iterBottom, sameLeftBottom, iterLeftBottom, sameRightBottom, iterRightBottom);
renderSilverRect(imin, imax, jmin, j,
   sameTop, iterTop, sameNewLine, iterNewLine, sameLeftTop, iterLeftTop, sameRightTop, iterRightTop);
}

}
}
else {
bool sameNewLine = true;
int i = imin+(imax-imin)/2;
//Compute new line
int iterNewLine = mandelIterate(i, jmin+1);
for (int k=jmin+2; k<jmax; k++) {
if (mandelIterate(i, k) != iterNewLine) {
sameNewLine = false;
}
}
//Check Top and Bottom for equality
bool sameRightTop = true;
bool sameLeftTop = true;
bool sameLeftBottom = true;
bool sameRightBottom = true;
int iterRightTop = fractalCanvas[i*height + jmin];
int iterLeftTop = fractalCanvas[imin*height + jmin];
int iterRightBottom = fractalCanvas[i*height + jmax];
int iterLeftBottom = fractalCanvas[imin*height + jmax];
if (!sameTop) {
for (int k=i+1; k<imax; k++) {
if (fractalCanvas[k*height + jmin] != iterRightTop) {
sameRightTop = false;
break;
//hier gebeurt het
}
}
for (int k=imin+1; k<i; k++) {
if (fractalCanvas[k*height + jmin] != iterLeftTop) {
sameLeftTop = false;
break;
}
}
}
if (!sameBottom) {
for (int k=i+1; k<imax; k++) {
if (fractalCanvas[k*height + jmax] != iterRightBottom) {
sameRightBottom = false;
break;
//hier gebeurt het
}
}
for (int k=imin+1; k<i; k++) {
if (fractalCanvas[k*height + jmax] != iterLeftBottom) {
sameLeftBottom = false;
break;
}
}
}
// void Render::renderSilverRect(int imin, int imax, int jmin, int jmax,
// bool sameTop, int iterTop, bool sameBottom, int iterBottom, bool sameLeft, int iterLeft, bool sameRight, int iterRight)
if (threadCount < NUMBER_OF_THREADS) {
thread t1(&Render::renderSilverRect, this, imin, i,    jmin, jmax,
   sameLeftTop, iterLeftTop, sameLeftBottom, iterLeftBottom, sameLeft, iterLeft, sameNewLine, iterNewLine);
thread t2(&Render::renderSilverRect, this, i, imax, jmin, jmax,
   sameRightTop, iterRightTop, sameRightBottom, iterRightBottom, sameNewLine, iterNewLine, sameRight, iterRight);
threadCount += 2;
t1.join(); threadCount--;
t2.join(); threadCount--;
}
else {
if(width-imax < 0.5*width) {
renderSilverRect(imin, i, jmin, jmax,
  sameLeftTop, iterLeftTop, sameLeftBottom, iterLeftBottom, sameLeft, iterLeft, sameNewLine, iterNewLine);
renderSilverRect(i, imax, jmin, jmax,
  sameRightTop, iterRightTop, sameRightBottom, iterRightBottom, sameNewLine, iterNewLine, sameRight, iterRight);
}
else {
renderSilverRect(i, imax, jmin, jmax,
  sameRightTop, iterRightTop, sameRightBottom, iterRightBottom, sameNewLine, iterNewLine, sameRight, iterRight);
renderSilverRect(imin, i, jmin, jmax,
  sameLeftTop, iterLeftTop, sameLeftBottom, iterLeftBottom, sameLeft, iterLeft, sameNewLine, iterNewLine);
}
}
}
pixelGroupings += 2;
}

void Render::renderSilverFull() {
//general case
int imin = 0;
int imax = width-1;
int jmin = 0;
int jmax = height-1;
bool sameRight = true;
bool sameLeft = true;
bool sameTop = true;
bool sameBottom = true;
int iterLeft = mandelIterate(imin, jmin);
int iterRight = mandelIterate(imax, jmin+1);
int iterTop = mandelIterate(imin+1, jmin);
int iterBottom = mandelIterate(imin, jmax);
int k;
for (k=imin+1; k<=imax; k++) {
if (mandelIterate(k, jmin) != iterTop) { //top
sameTop = false;
}
if (mandelIterate(k-1, jmax) != iterBottom) { //bottom
sameBottom = false;
}
}
for (k=jmin+1; k<=jmax; k++) {
if (mandelIterate(imin, k-1) != iterLeft) { //left
sameLeft = false;
}
if (mandelIterate(imax, k) != iterRight) { //right
sameRight = false;
}
}
/*
if (sameRight && sameLeft && sameTop && sameBottom && x_range > 3.95) {
sameRight = false; //force subdivision of the main tile
}
*/
renderSilverRect(imin, imax, jmin, jmax, sameTop, iterTop, sameBottom, iterBottom, sameLeft, iterLeft, sameRight, iterRight);
}

void Render::start() {
threadCount = 0;
pixelGroupings = 0;
InvalidateRect(hWnd, NULL, TRUE);
hdc = BeginPaint(hWnd, &ps);
renderSilverFull();
if (guessingViewType == 1) {
loadFractalCanvas(true);
InvalidateRect(hWnd, NULL, TRUE);
}
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
EndPaint(hWnd, &ps);
}

void multithreadRender(HWND hWnd) {
int renderID = ++lastRenderID;

printf("-----New Render-----\n"
"renderID: %d\n"
"center: %f + %fi\n", renderID, cimag(center), creal(center));

auto start = chrono::high_resolution_clock::now();

if (formulaType != FORMULA_BURNING_SHIP) { // && x_range < 4.05) {
Render renderTask(renderID, hWnd);
renderTask.start();
printf("guessedPixelCount: %d / %d\n", renderTask.guessedPixelCount, width*height);
printf("pixelGroupings: %d\n", renderTask.pixelGroupings);
}
else {
PAINTSTRUCT ps;
InvalidateRect(hWnd, NULL, TRUE);
HDC hdc = BeginPaint(hWnd, &ps);
{
int j=0;
int i=0;
for (i=0; i<width; i++) {
    for (j=0; j<height; j++) {
    if(i%150 == 0 && j%150 == 0) {
    DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
    }
double _Complex c;
double _Complex z;
int iterationCount = 0;
if (julia) {
c = juliaSeed;
z = inflections(transformation(map(i, j)));
}
else {
c = inflections(transformation(map(i, j)));
z = 0;
}
if (renderID == lastRenderID) {
while (creal(z)*creal(z) + cimag(z)*cimag(z) < 4 && iterationCount < maxIters) {
z = formula(z, c);
iterationCount++;
}
}
if (iterationCount >= maxIters) {
ptPixels[width*(height-j-1) + i] = RGB(0, 0, 0);
}
else {
ptPixels[width*(height-j-1) + i] = gradient(iterationCount);
}
}
}
}
if (guessingViewType == 1) {
loadFractalCanvas(true);
InvalidateRect(hWnd, NULL, TRUE);
}
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
EndPaint(hWnd, &ps);
}
auto finish = chrono::high_resolution_clock::now();
chrono::duration<double> elapsed = finish - start;
cout << "Elapsed time: " << elapsed.count() << " s\n";
return;
}

void startRender(HWND hWnd) {
thread t(multithreadRender, hWnd);
t.detach();
}


///VNAF HIER ALLEMAAL ONZINCODE DIE TE MAKEN HEEFT MET VENSTERS MAKEN


int DisplayConfirmSaveAsMessageBox()
{
    int msgboxID = MessageBox(
        NULL,
        "temp.txt already exists.\nDo you want to replace it?",
        "Confirm Save As",
        MB_ICONEXCLAMATION | MB_YESNO
    );

    if (msgboxID == IDYES)
    {
        // TODO: add code
    }

    return msgboxID;
}
const int IDD_DIALOG12 = 116;
/*
HWND editBox = CreateWindowEx(WS_EX_PALETTEWINDOW, TEXT("Edit"), "honden",
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT , width, 0, 150, 20, hWnd, (HMENU) hInst, NULL, NULL);

HWND editBox = CreateWindow(
szWindowClass,
TEXT("Edit"),
WS_OVERLAPPEDWINDOW | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
CW_USEDEFAULT, CW_USEDEFAULT,
width+700, height+700,
NULL,
NULL,
hInst,
NULL
);
*/

HWND textinfo = NULL;
HWND editBox = CreateWindowEx(WS_EX_PALETTEWINDOW, TEXT("Edit"), "honden",
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT , width, 0, 150, 20, NULL, (HMENU) hInst, NULL, NULL);
HWND button =  NULL;
HWND test = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
// TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
switch (message)
{
case WM_ERASEBKGND:
return true;
case WM_CREATE: {
HDC hdc;
NUMBER_OF_THREADS = thread::hardware_concurrency() + 4;
printf("number of threads: %d\n", NUMBER_OF_THREADS);
if(NUMBER_OF_THREADS == 0) {
printf("Couldn't detect the number of cores (default to 12)\n");
NUMBER_OF_THREADS = 12;
}
//NUMBER_OF_THREADS = 0; //Single-threaded
BITMAPINFO RGB32BitsBITMAPINFO;
ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));
RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
RGB32BitsBITMAPINFO.bmiHeader.biWidth=width;
RGB32BitsBITMAPINFO.bmiHeader.biHeight=height;
RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;
screenBMP = CreateDIBSection(
hdc,
(BITMAPINFO *)&RGB32BitsBITMAPINFO,
DIB_RGB_COLORS,
(void**)&ptPixels,
NULL, 0
);
AddMenus(hWnd);


///MESSING AROUND WITH WINDOW CREATION.CLEAN THIS UP

TCHAR greeting[] = _T("Hello, World!");
HWND textinfo = CreateWindowEx(WS_EX_PALETTEWINDOW, "STATIC", "          mmmmmmmmmmmmm",
WS_VISIBLE | WS_CHILD, width, 0, 50, 50, hWnd, (HMENU) hInst, NULL, NULL);
HWND editBox = CreateWindowEx(WS_EX_PALETTEWINDOW, TEXT("Edit"), "testtext",
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT , width, 0, 150, 20, hWnd, (HMENU) hInst, NULL, NULL);
HWND button = CreateWindowEx(WS_EX_PALETTEWINDOW, "BUTTON", "Set",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, width, 300, 100, 50, hWnd, (HMENU) hInst, NULL, NULL);
HWND test = CreateWindowEx(WS_EX_PALETTEWINDOW, "DIALOGEX", "Set",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, width, 600, 100, 50, hWnd, (HMENU) hInst, NULL, NULL);
TextOut(hdc, 600, 300, greeting, _tcslen(greeting));


/*
Options = CreateDialog(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_DIALOG12),hWnd,(DLGPROC)OptionsProc);
ShowWindow(Options, SW_SHOW);
*/

//Important part of code again, this should not be removed:
reset();
startRender(hWnd);
break;
}
case WM_COMMAND: {
//menu selection
bool fractalTypeChange = true;
switch(LOWORD(wParam)) {//fractal types
case FORMULA_BURNING_SHIP:
case FORMULA_M_2:
inflectionPower = 2;
break;
case FORMULA_M_3:
inflectionPower = 3;
break;
case FORMULA_M_4:
inflectionPower = 4;
break;
case FORMULA_M_5:
inflectionPower = 5;
break;
default:
fractalTypeChange = false;
}
if (fractalTypeChange) {
formulaType = wParam;
reset();
startRender(hWnd);
break;
}
switch(LOWORD(wParam)) {//other menu buttons
case RESET:
reset();
startRender(hWnd);
break;
case TOGGLE_JULIA:
juliaSeed = center;
julia = !julia;
reset();
startRender(hWnd);
break;
case WINDOW_OPTIONS:
            ShowWindow(hOptions, SW_SHOW);
UpdateWindow(hOptions);
            break;
            case BN_CLICKED: {
            /*
            //char power[] = "01234";
            int cTxtLen = GetWindowTextLength(editBox);
            PSTR power = (PSTR) VirtualAlloc((LPVOID) NULL, (DWORD) (cTxtLen + 1), MEM_COMMIT, PAGE_READWRITE);
            cout << "windowtext func: " << GetWindowText(editBox, power, cTxtLen+1) << endl;
            cout << "last error" << GetLastError() << endl;
            //for (int i=0; i<5; i++) {
            // cout << power[i];
            //}
            cout << power << " is de macht" << endl;
            if (editBox == NULL) {
            cout << "is null\n";
            }
            */
            //transformation_type = (transformation_type+1)%6;
            break;
            }
case VIEW_GUESSED_PIXELS: {
guessingViewType = (guessingViewType+1)%2;
loadFractalCanvas(guessingViewType%2);
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;
}
case QUIT:
PostQuitMessage(0);
break;
}
break;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if (firstPaint) {
InvalidateRect(hWnd, NULL, TRUE);
firstPaint = false;
}
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOWTEXT+1));
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
EndPaint(hWnd, &ps);
break;
}
case WM_MOUSEWHEEL: {
//zoom action
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
POINT point = {xPos, yPos};
ScreenToClient(hWnd, &point);
xPos = point.x;
yPos = point.y;
if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
return 0;
}
int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
//generate preview bitmap
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
ptPixelsTMP[width*j + i] = ptPixels[width*j + i];
}
}
int iNew;
int jNew;
if (zDelta > 0) { //zoom in
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
iNew = xPos - (xPos - i)/4;
jNew = height - (yPos - (yPos - j)/4) - 1;
if (iNew>= 0 && iNew<width && jNew>=0 && jNew<height) {
ptPixels[width*(height-j-1) + i] = ptPixelsTMP[width*jNew + iNew];
}
else {
ptPixels[width*j + i] = RGB(0, 0, 0);
}
}
}
}
else { //zoom out
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
/* for zooming
iNew = xPos + 4*i - 2*width;
jNew = 3*height - (yPos + 4*j) - 1;
*/
iNew = xPos - (xPos - i)*4;
jNew = height - (yPos - (yPos - j)*4) - 1;
if (iNew>= 0 && iNew<width && jNew>=0 && jNew<height) {
ptPixels[width*(height-j-1) + i] = ptPixelsTMP[width*jNew + iNew];
}
else {
ptPixels[width*j + i] = RGB(0, 0, 0);
}
}
}
}
//draw preview
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
//set new parameters and startRender
//center = (map(xPos, yPos)+(map(xPos, yPos)+center)/2)/2; //working formula, albeit can be simplified
//the way this works is applying the known transformation for a zoom size of 2, 2 times.
//for the other direction: 2*(2*center-map(xPos, yPos))-map(xPos, yPos) is two times the inverse transformation
//center = map(xPos, yPos); //old method
if (zDelta > 0) {
center = 0.75*map(xPos, yPos)+0.25*center;
x_range = x_range/4;
y_range = y_range/4;
}
else {
center = 4*center - 3*map(xPos, yPos);
x_range *= 4;
y_range *= 4;
}
startRender(hWnd);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
EndPaint(hWnd, &ps);
printf("xPos: %d\n", xPos);
printf("yPos: %d\n", yPos);
printf("xrange: %f\n", x_range);
printf("yrange: %f\n", y_range);
break;
}
case WM_LBUTTONUP: {
//create inflection
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
return 0;
}
if(true || inflectionCount < MAX_NUMBER_OF_INFLECTIONS) {
inflectionCoords[inflectionCount] = map(xPos, yPos);
inflectionCount += 1;
center = 0 + 0*I;
x_range = 4;
y_range = x_range*((double)height/(double)width);
printf("inflection coords:\n");
for (int i=0; i<inflectionCount; i++) {
printf("i=%d", i);printf(": %f ", creal(inflectionCoords[i]));printf("+ %f * I\n", cimag(inflectionCoords[i]));
}
startRender(hWnd);
}
else {
MessageBoxW(NULL, L"No more inflections can be set.", L"Problem", MB_OK);
}
break;
}
case WM_RBUTTONUP: {
//remove inflection
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
return 0;
}
if (inflectionCount > 0)  {
inflectionCount--;
startRender(hWnd);
}
break;
}
case WM_DESTROY: 
PostQuitMessage(0); 
break; 
default:
return DefWindowProc(hWnd, message, wParam, lParam); 
break; 
}
return 0;
}


LRESULT CALLBACK OptionsProc(HWND hOptions, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message)
{
case WM_CREATE: {
ShowWindow(hOptions, SW_SHOW);
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hOptions, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hOptions, &ps);
break;
}
case WM_DESTROY:
break;
default:
return DefWindowProc(hOptions, message, wParam, lParam); 
break; 
}
return 0;
}


Linkback: https://fractalforums.org/index.php?topic=777.0
« Last Edit: April 12, 2021, 11:09:06 PM by Dinkydau »

Offline gerrit

  • 3f
  • ******
  • Posts: 2469
Re: Explore fractals (inflection tool)
« Reply #1 on: January 30, 2018, 02:13:42 AM »
Thanks. Seems it's missing a dll.

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Re: Explore fractals (inflection tool)
« Reply #2 on: January 30, 2018, 02:45:23 AM »
I see. Sorry for the inconvenience. I removed the part that required the DLL and tested the new executable in my VM that has almost nothing installed, so I think it should work now. The link has been updated.
« Last Edit: January 30, 2018, 04:37:10 AM by Dinkydau »

Offline gerrit

  • 3f
  • ******
  • Posts: 2469
Re: Explore fractals (inflection tool)
« Reply #3 on: January 30, 2018, 03:11:23 AM »
Run now, thanks. Now I have to figure out how it works.
If I just click near top of M-set I get something with 5-fold symmetry. How is that possible?

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Re: Explore fractals (inflection tool)
« Reply #4 on: January 30, 2018, 04:35:54 AM »
Oh yeah this is stupid. I broke this while trying to make the code a little better before publishing. Changing the fractal type sets the inflection power to 5. Hopefully it works now.

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Re: Explore fractals (inflection tool)
« Reply #5 on: March 08, 2019, 02:17:15 AM »
It's also slow, compared to fractal extreme, for example. I don't know how to improve the speed.

It turns out it's not actually that slow compared to fractal extreme. When the location is dense, with many iterations, causing the program to spend almost all its time in the iteration loop, it's only 3,2 to 3,6 times slower.

Code: [Select]
while (zrsqr + zisqr <= 4.0 && iterationCount < maxIters) {
zi = zr*zi;
zi += zi;
zi += ci;
zr = zrsqr - zisqr + cr;
zrsqr = zr*zr;
zisqr = zi*zi;
iterationCount++;
}

So that loop is not the problem. It's everything around it: updating the image, managing threads, reading memory... those things are taking up almost all the time at easy locations. I want to see if I can do something about it and improve the program in other ways as well. Testing the result of inflections has become an important part of designing julia morphings for me and I use this program a lot so even if no one else uses it it's probably worth spending more time on to make it more user friendly.

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Re: Explore fractals (inflection tool)
« Reply #6 on: April 05, 2019, 12:15:38 AM »
New version:



ExploreFractals.exe   518 KB
https://mega.nz/#!Z88Q1QiI!pwRXrL6c3UN9ujurfthA0A6uPX9j-Z8wej4w0aomeYQ
CRC32: 76357D65
MD5: 2915B31F8CC4C15B0F3EA293AFC63A22

Changes:
1. options window with various settings among which: image size, oversampling, gradient speed and offset
2. more efficiency (many changes there), especially Mandelbrot with power 2
3. checkerboard "formula" to simulate tilings and an extremely high power Mandelbrot set (power is 33554432 = 2^25) (slow)
4. changes with the intention of improving the reliability, responsiveness and flexibility of the program for later
5. some small changes such as a different gradient

I think the program feels faster and it now has some basic settings that the previous (and first) version lacked. You probably shouldn't use the 16×16 oversampling though. The program can't really handle it well.

Hint: use "change transformation" while in checkers mode (you need to hit that menu option 6 times to get the right one) to get a logarithmic transformation of the plane. It corresponds with how tilings in the Mandelbrot set occur.

Hint 2: this program is not intended for deep zooming and rendering beautiful images. It's only a tool to test inflections (which you can add simply by clicking). I also use it as a framework for experiments which is why there are some strange features.

Known problems:
1. Changing the resolution during a render may crash the program.
2. Multiple options windows can be opened which causes some of them to stop working.
3. The only way to navigate is with a scroll wheel.

Code (C++, can be compiled with both gcc and visual studio):
Code: [Select]
//WinApi
#include <Windowsx.h>
#include <windows.h>
#include <CommCtrl.h>

//C++ standard library
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <tchar.h>
#include <complex>
#include <math.h>
#include <time.h>
#include <chrono>
#include <thread>
#include <random>
#include <string>
#include <vector>

/*
//Other include
#include <gmp.h>
#include <gmpxx.h>
*/

//Link to libraries
//#pragma comment(lib,"comctl32.lib") //see line 107. is this important?
//#pragma comment(lib,"C:/msys64/mingw64/lib/libgmp.dll.a")

//Macro
#define RGB2(r,g,b) RGB(b,g,r) //once placed in a bitmap, colors are displayed in reverse order for some reason.
#define CUSTOM_REFRESH 8000 //custom message

using namespace std;

typedef std::complex<double> double_c;
const double_c I(0, 1);

std::mt19937_64 generator;
uniform_real_distribution<double> distribution(0.0, 1.0);

double random() {
return distribution(generator);
}

struct iterData {
int iterationCount;
bool guessed;
bool inMinibrot;
};

HINSTANCE hInst; //application instance
HWND hWndMain; //main window
HWND hOptions; //options window

static TCHAR szWindowClass[] = _T("win32app"); // The main window class name. 
static TCHAR szTitle[] = _T("Explore fractals"); // The string that appears in the application's title bar. 

static TCHAR TITLE_OPTIONS[] = _T("Options");
static TCHAR CLASS_OPTIONS[] = _T("Options Window");

// Forward declarations of window procedure functions
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK OptionsProc(HWND, UINT, WPARAM, LPARAM);

int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int    nCmdShow
)
{
setbuf(stdout, NULL);

/*
INITCOMMONCONTROLSEX icce;
icce.dwSize = sizeof(INITCOMMONCONTROLSEX);
icce.dwICC = ICC_WIN95_CLASSES;

//InitCommonControlsEx(&icce);
InitCommonControls();
*/

WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
//wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* Load a standard icon */
wcex.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(1100)); /* Load a standard icon */
//wcex.hIcon = (HICON)LoadImageA(hInst, "R.ico", IMAGE_ICON, 152, 152, LR_LOADFROMFILE);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); /* use the name "A" to use the project icon */
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
20, 70,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd) {
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
hWndMain = hWnd;

//Register options window
WNDCLASSEX optionsEx;
optionsEx.cbSize = sizeof(WNDCLASSEX);
optionsEx.style = CS_HREDRAW | CS_VREDRAW;
optionsEx.lpfnWndProc = OptionsProc;
optionsEx.cbClsExtra = 0;
optionsEx.cbWndExtra = 0;
optionsEx.hInstance = hInstance;
optionsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* Load a standard icon */
optionsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
optionsEx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
optionsEx.lpszMenuName = NULL;
optionsEx.lpszClassName = CLASS_OPTIONS;
optionsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); /* use the name "A" to use the project icon */
if (!RegisterClassEx(&optionsEx)) {
cout << "error: " << GetLastError() << endl;
MessageBox(NULL,
_T("Call to RegisterClassEx failed for optionsEx!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
//end register options window

hInst = hInstance;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return (int)msg.wParam;
}

int createOptionsWindow() {
hOptions = CreateWindow(CLASS_OPTIONS, TITLE_OPTIONS, WS_OVERLAPPEDWINDOW, 0, 0, 630, 470, hWndMain, NULL, hInst, NULL);
if (!hOptions) {
cout << "error: " << GetLastError() << endl;
MessageBox(NULL,
_T("Call to CreateWindow failed for hOptions!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
ShowWindow(hOptions, SW_SHOW);
UpdateWindow(hOptions);
return 0;
}

HWND WINAPI CreateTrackbar(
HWND hwndDlg,  // handle of dialog box (parent window)
UINT selMin,     // minimum value in trackbar range
UINT selMax,     // maximum value in trackbar range
int xPos, int yPos,
int hSize, int vSize,
int identifier)
{
HWND hwndTrack = CreateWindowEx(
0,                               // no extended styles
TRACKBAR_CLASS,                  // class name
_T("Trackbar Control"),              // title (caption)
WS_CHILD |
WS_VISIBLE |
TBS_AUTOTICKS |
TBS_ENABLESELRANGE,              // style
xPos, yPos,                          // position
hSize, vSize,                         // size
hwndDlg,                         // parent window
(HMENU)identifier,                     // control identifier
hInst,                         // instance
NULL                             // no WM_CREATE parameter
);
SendMessage(hwndTrack, TBM_SETRANGE,
(WPARAM)TRUE,                   // redraw flag
(LPARAM)MAKELONG(selMin, selMax));  // min. & max. positions
SendMessage(hwndTrack, TBM_SETPAGESIZE,
0, (LPARAM)4);                  // new page size
SendMessage(hwndTrack, TBM_SETPOS,
(WPARAM)TRUE,                   // redraw flag
(LPARAM)selMin);
SetFocus(hwndTrack);
return hwndTrack;
}

//Menu options
const int QUIT = 1;
const int RESET = 2;
const int TOGGLE_JULIA = 3;
const int FORMULA_M_2 = 4;
const int FORMULA_BURNING_SHIP = 5;
const int FORMULA_M_3 = 6;
const int FORMULA_M_4 = 7;
const int FORMULA_M_5 = 8;
const int FORMULA_CHECKERS = 12;
const int VIEW_GUESSED_PIXELS = 9;
const int WINDOW_OPTIONS = 10;
const int FORMULA_TEST = 11;
const int FORMULA_TEST_CONTROL = 15;
const int FORMULA_HIGH_POWER = 13;
const int CHANGE_TRANSFORMATION = 14;

void AddMenus(HWND hwnd) {

HMENU hMenubar;
HMENU hMenuOther;

hMenubar = CreateMenu();
hMenuOther = CreateMenu();
//hMenuOther, item type, message (number), button text

AppendMenuA(hMenubar, MF_STRING, WINDOW_OPTIONS, "&Options");
AppendMenuA(hMenubar, MF_STRING, RESET, "&Reset");
AppendMenuA(hMenubar, MF_STRING, TOGGLE_JULIA, "&Toggle Julia");
AppendMenuA(hMenubar, MF_STRING, FORMULA_M_2, "&Mandelbrot");
AppendMenuA(hMenubar, MF_STRING, FORMULA_M_3, "&Mandelbrot power 3");
AppendMenuA(hMenubar, MF_STRING, FORMULA_CHECKERS, "Checkers&");

AppendMenuA(hMenuOther, MF_STRING, CHANGE_TRANSFORMATION, "&Change transformation");
AppendMenuA(hMenuOther, MF_STRING, VIEW_GUESSED_PIXELS, "&View guessed pixels");
AppendMenuA(hMenuOther, MF_SEPARATOR, 0, NULL);
AppendMenuA(hMenuOther, MF_STRING, FORMULA_BURNING_SHIP, "&Burning Ship");
AppendMenuA(hMenuOther, MF_STRING, FORMULA_M_4, "Mandelbrot power 4&");
AppendMenuA(hMenuOther, MF_STRING, FORMULA_M_5, "Mandelbrot power 5&");
AppendMenuA(hMenuOther, MF_STRING, FORMULA_TEST, "Test formula&");
AppendMenuA(hMenuOther, MF_STRING, FORMULA_TEST_CONTROL, "Test formula 2 (control)&");
AppendMenuA(hMenuOther, MF_STRING, FORMULA_HIGH_POWER, "High Power Mandelbrot&");
AppendMenuA(hMenuOther, MF_SEPARATOR, 0, NULL);
AppendMenuA(hMenuOther, MF_STRING, QUIT, "&Quit");
AppendMenuA(hMenubar, MF_POPUP, (UINT_PTR)hMenuOther, "&More");
SetMenu(hwnd, hMenubar);
}

//Global variables that should not be user influenceable
const int MAX_NUMBER_OF_INFLECTIONS = 500;
const int MAXRES_WIDTH = 20000;
const int MAXRES_HEIGHT = 20000;
unsigned NUMBER_OF_THREADS;
bool firstPaint = true;

//Fractal global variables
int width = 1199;
int height = 849;
int screenWidth = width;
int screenHeight = height;
int oversampling = width / screenWidth;
double rotation = 0;
double_c center;
double_c topleftCorner;
double x_range = 4;
double y_range = 4;
double pixelWidth;
double pixelHeight;
int maxIters = 4600;
double bailout = 400;
double_c juliaSeed = -0.75 + 0.1*I;
bool julia = false;
HBITMAP screenBMP;
UINT * ptPixels = (UINT*)malloc(0); //bitmap colors representing the iteration data
iterData * fractalCanvas = (iterData*)calloc(width*height, sizeof(iterData)); //iteration counts
int lastRenderID = 0;

const int NUMBER_OF_COLORS = 4; //must be power of 2 to use AND to calculate the color array index
double gradientSpeed = 17.0;
double gradientOffset = 0;

double transferFunction(double gradientSpeedd) {
return pow(1.1, gradientSpeedd - 1);
}

double gradientSpeedFactor = 1 / transferFunction(gradientSpeed);
int gradientOffsetTerm = 0;

bool setGradientOffset(double newGradientOffset) {
if (newGradientOffset >= 0.0 && newGradientOffset <= 1.0 && gradientOffset != newGradientOffset) {
gradientOffset = newGradientOffset;
int interpolatedGradientLength = NUMBER_OF_COLORS * transferFunction(gradientSpeed);
gradientOffsetTerm = interpolatedGradientLength * gradientOffset;
return true;
}
return false;
}

bool setGradientSpeed(double newGradientSpeed) {
if (newGradientSpeed >= 0.0 && newGradientSpeed != gradientSpeed) {
gradientSpeed = newGradientSpeed;
double computedGradientSpeed = transferFunction(gradientSpeed);
gradientSpeedFactor = 1.0 / computedGradientSpeed;
gradientOffsetTerm = NUMBER_OF_COLORS * computedGradientSpeed * gradientOffset;
return true;
}
return false;
}
UINT* gradientColors = (UINT*)malloc(NUMBER_OF_COLORS * sizeof(UINT));

bool setGradientColors() {
gradientColors[0] = RGB(255, 255, 255);
gradientColors[1] = RGB(52, 140, 167);
gradientColors[2] = RGB(0, 0, 0);
gradientColors[3] = RGB(229, 140, 45);
return true;
}
bool isSet = setGradientColors();

UINT inline gradient(int iterationCount) {
double gradientPosition = (iterationCount + gradientOffsetTerm) * gradientSpeedFactor;
UINT asInt = (UINT)gradientPosition;
UINT previousColor = gradientColors[asInt & (NUMBER_OF_COLORS - 1)];
UINT nextColor = gradientColors[(asInt + 1) & (NUMBER_OF_COLORS - 1)];
double ratio = gradientPosition - asInt;
return RGB2(
(unsigned char)((GetRValue(previousColor))*(1 - ratio) + (GetRValue(nextColor))*ratio),
(unsigned char)((GetGValue(previousColor))*(1 - ratio) + (GetGValue(nextColor))*ratio),
(unsigned char)((GetBValue(previousColor))*(1 - ratio) + (GetBValue(nextColor))*ratio)
);
}

double getZoomLevel() {
return -log2(x_range) + 2;
}

bool setMaxIters(int newMaxIters) {
if (newMaxIters < 1 || newMaxIters == maxIters)
return false;
maxIters = newMaxIters;
return true;
}

/*
void inline setPixel(int i, int j, int iterationCount, bool isGuessed) {
//only for debug this if:
//if (i < 0 || j < 0 || i > width || j > height || iterationCount < 0) {
// cout << "Setting pixel failed at location i: " << i << "  " << "j: " << j << endl;
// return false;
//}
bool isInMinibrot = iterationCount >= maxIters;
fractalCanvas[i*height + j] = {
iterationCount,
isGuessed,
isInMinibrot
};
if (isInMinibrot) ptPixels[width*(height - j - 1) + i] = RGB2(0, 0, 0);
else   ptPixels[width*(height - j - 1) + i] = gradient(iterationCount);
}
*/

#define ISCALCULATED false
#define ISGUESSED true

#define setPixel(i,j,iterationCount,isGuessed) \
bool __isInMinibrot = iterationCount >= maxIters; \
fractalCanvas[i*height + j] = { \
iterationCount, \
isGuessed, \
__isInMinibrot \
}; \
ptPixels[width*(height - j - 1) + i] = (__isInMinibrot ? RGB2(0, 0, 0) : gradient(iterationCount));

int inline getPixel(int i, int j) {
return fractalCanvas[i*height + j].iterationCount;
}

void loadFractalCanvas(bool falseColor) {
iterData it;
if (falseColor) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
it = fractalCanvas[i*height + j];
if (it.inMinibrot && !it.guessed) ptPixels[width*(height - j - 1) + i] = RGB2(255, 0, 0);
else if (it.inMinibrot)   ptPixels[width*(height - j - 1) + i] = RGB2(0, 0, 255);
else if (it.guessed)   ptPixels[width*(height - j - 1) + i] = RGB2(0, 255, 0);
else   ptPixels[width*(height - j - 1) + i] = gradient(it.iterationCount);
}
}
}
else {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
it = fractalCanvas[i*height + j];
if (it.inMinibrot)   ptPixels[width*(height - j - 1) + i] = RGB2(0, 0, 0);
else   ptPixels[width*(height - j - 1) + i] = gradient(it.iterationCount);
}
}
}
}

BOOL DrawBitmap(HDC hDC, int x, int y, HBITMAP hBitmap, DWORD dwROP) {
HDC hDCBits;
BOOL bResult;
if (!hDC || !hBitmap)
return FALSE;
hDCBits = CreateCompatibleDC(hDC);
SelectObject(hDCBits, hBitmap);
if (width != screenWidth || height != screenHeight) {
cout << "screenwidth != width (or height) " << endl;
SetStretchBltMode(hDC, HALFTONE);
bResult = StretchBlt(hDC, x, y, screenWidth, screenHeight, hDCBits, 0, 0, width, height, dwROP);
}
else {
bResult = BitBlt(hDC, x, y, width, height, hDCBits, 0, 0, dwROP);
}
DeleteDC(hDCBits);
return bResult;
}

void refreshBitmap(bool viewGuessedPixels) {
InvalidateRect(hWndMain, NULL, TRUE);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWndMain, &ps);
loadFractalCanvas(viewGuessedPixels);
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
EndPaint(hWndMain, &ps);
}

double_c inline map(int i, int j) {
//Maps a pixel at location (i, j) in the window to a complex number.
//return (real(center) - x_range / 2 + i * (x_range / width)) +
//    (imag(center) - y_range / 2 + j * (y_range / height)) * I;
return topleftCorner + i * pixelWidth - j * pixelHeight*I;
}

int formulaType = FORMULA_M_2;
double_c formula(double_c z, double_c c) {
switch (formulaType) {
case FORMULA_M_2:
return pow(z, 2) + c;
case FORMULA_M_3:
return pow(z, 3) + c;
case FORMULA_M_4:
return pow(z, 4) + c;
case FORMULA_M_5:
return pow(z, 5) + c;
case FORMULA_BURNING_SHIP:
return pow((abs(real(z)) + abs(imag(z))*I), 2) + c;
case FORMULA_TEST:
return z * z + c + (random()*0.000001);
case FORMULA_HIGH_POWER:
return pow(z, 33554432) + c;
}
}

int transformation_type = 0;
const int NUMBER_OF_TRANSFORMATIONS = 6 + 1;
double_c transformation(double_c c) {
switch (transformation_type) {
case 0:
return c;
case 1: {
double_c z = 0;
for (int i = 0; i < 5; i++) {
z = z * z + c;
}
return z;
}
case 2:
return cos(c);
case 3:
return c + 2. + 2.*I;
case 4:
return sqrt(c);
case 5:
return sqrt(sqrt(c));
case 6:
return log(c);
}
}

double_c* inflectionCoords = (double_c*)malloc(MAX_NUMBER_OF_INFLECTIONS * sizeof(double_c));
int inflectionCount = 0;
int inflectionPower = 2;
double_c inline inflections(double_c c) {
for (int i = inflectionCount - 1; i >= 0; i--) {
c = pow(c, inflectionPower) + inflectionCoords[i];
}
return c;
}

bool setCenterAndZoom(double_c newCenter, double zoom) {
//Set both of these settings together because the zoom level, topleftCorner coordinate and pixel size are related.

auto setRenderRange = [](double zoom) {
double x_range_new = 4 / pow(2, zoom);
double y_range_new = x_range_new * ((double)height / (double)width);
if (x_range_new != x_range || y_range_new != y_range) {
x_range = x_range_new;
y_range = y_range_new;
return true;
}
return false;
};

auto setCoordinates = [](double_c newCenter) {
bool recalcRequired = (center != newCenter);
center = newCenter;
topleftCorner = center - x_range / 2 + (y_range / 2)*I;
return recalcRequired;
};

auto updatePixelSize = []() {
double newPixelWidth = x_range / width;
double newPixelHeight = y_range / height;
if (newPixelHeight != pixelHeight || newPixelWidth != pixelWidth) {
pixelWidth = newPixelWidth;
pixelHeight = newPixelHeight;
return true;
}
return false;
};

bool recalcRequired = false;
//This order is important:
recalcRequired |= setRenderRange(zoom);
recalcRequired |= setCoordinates(newCenter);
recalcRequired |= updatePixelSize();

return recalcRequired;
}

bool updateCenterAndZoom() {
return setCenterAndZoom(center, getZoomLevel());
}

class Render {
private:
int renderID;
int threadCount;
public:
int guessedPixelCount;
int pixelGroupings;
Render(int);
~Render();
int mandelIterate(int, int);
bool calcHorizontalLine(int, int, int);
bool calcVerticalLine(int, int, int);
void renderSilverRect(int, int, int, int, bool, int, bool, int, bool, int, bool, int);
void renderSilverFull();
void start();
};

Render::Render(int currentRenderID) {
renderID = currentRenderID;
}

Render::~Render(void) {
cout << "Render is being deleted" << endl;
}

int Render::mandelIterate(int i, int j) {
int iterationCount = 0;
if (formulaType == FORMULA_M_2) {
double_c c = transformation(inflections(map(i, j)));

double cr;
double ci;
double zr;
double zi;
double zrsqr;
double zisqr;
if (julia) {
cr = real(juliaSeed);
ci = imag(juliaSeed);
zr = real(c);
zi = imag(c);
zrsqr = zr * zr;
zisqr = zi * zi;
}
else {
double zx;
double zy;
zx = real(c);
zy = imag(c);
double zx2 = zx;
double zy2 = zy;

zx -= 0.25; zy *= zy;
double q = zx * zx + zy;
if (4 * q*(q + zx) < zy) {
setPixel(i, j, maxIters, ISCALCULATED);
return maxIters;
}
zx = zx2;
zy = zy2;
zx++;
if (zx * zx + zy * zy < 0.0625) {
setPixel(i, j, maxIters, ISCALCULATED);
return maxIters;
}

cr = zx2;
ci = zy2;
zr = 0;
zi = 0;
zrsqr = 0;
zisqr = 0;
}
while (zrsqr + zisqr <= 4.0 && iterationCount < maxIters) {
zi = zr * zi;
zi += zi;
zi += ci;
zr = zrsqr - zisqr + cr;
zrsqr = zr * zr;
zisqr = zi * zi;
iterationCount++;
}
}
else {
double_c c;
double_c z;
if (julia) {
c = juliaSeed;
z = transformation(inflections(map(i, j)));
}
else {
c = transformation(inflections(map(i, j)));
z = 0;
}
while (real(z)*real(z) + imag(z)*imag(z) < bailout && iterationCount < maxIters) {
z = formula(z, c);
iterationCount++;
}
}
setPixel(i, j, iterationCount, ISCALCULATED);
return iterationCount;
}

void Render::renderSilverRect(int imin, int imax, int jmin, int jmax,
bool sameTop, int iterTop, bool sameBottom, int iterBottom, bool sameLeft, int iterLeft, bool sameRight, int iterRight)
{
if (renderID != lastRenderID) {
printf("Render cancelled; terminating thread\n");
return;
}
if (sameRight && sameLeft && sameTop && sameBottom && iterRight == iterTop && iterTop == iterLeft && iterLeft == iterBottom && iterRight != 1 && iterRight != 0) {
//Fill with guessed pixels
for (int i = imin + 1; i < imax; i++) {
for (int j = jmin + 1; j < jmax; j++) {
setPixel(i, j, iterLeft, ISGUESSED);
}
}
guessedPixelCount += (imax - imin - 1)*(jmax - jmin - 2);
return;
}
if (imin >= imax - 4 || jmin >= jmax - 4) {
//The tile is now very small. Stop the recursion and iterate all pixels.
for (int i = imin; i <= imax; i++) {
for (int j = jmin; j <= jmax; j++) {
mandelIterate(i, j);
}
}
return;
}
if (imax - imin < jmax - jmin) { // || threadCount < NUMBER_OF_THREADS) {
bool sameNewLine = true;
int j = jmin + (jmax - jmin) / 2;
//compute new line
int iterNewLine = mandelIterate(imin + 1, j);
for (int k = imin + 2; k < imax; k++) {
if (mandelIterate(k, j) != iterNewLine) {
sameNewLine = false;
}
}
//check right and left for equality
bool sameRightTop = true;
bool sameLeftTop = true;
bool sameLeftBottom = true;
bool sameRightBottom = true;
int iterRightTop = getPixel(imax, jmin);// fractalCanvas[imax*height + jmin];
int iterRightBottom = getPixel(imax, j);// fractalCanvas[imax*height + j];
int iterLeftTop = getPixel(imin, jmin);// fractalCanvas[imin*height + jmin];
int iterLeftBottom = getPixel(imin, j);// fractalCanvas[imin*height + j];
if (!sameRight) {
for (int k = jmin + 1; k <= j; k++) {
//if (fractalCanvas[imax*height + k] != iterRightTop) {
if (getPixel(imax, k) != iterRightTop) {
sameRightTop = false;
break;
}
}
for (int k = j + 1; k <= jmax; k++) {
//if (fractalCanvas[imax*height + k] != iterRightBottom) {
if (getPixel(imax, k) != iterRightBottom) {
sameRightBottom = false;
break;
}
}
}
if (!sameLeft) {
for (int k = jmin + 1; k <= j; k++) {
//if (fractalCanvas[imin*height + k] != iterLeftTop) {
if (getPixel(imin, k) != iterLeftTop) {
sameLeftTop = false;
break;
}
}
for (int k = j + 1; k <= jmax; k++) {
if (getPixel(imin, k) != iterLeftBottom) {
sameLeftBottom = false;
break;
}
}
}
//cout << "threadcount: " << threadCount << endl;
//if (true || threadCount < NUMBER_OF_THREADS) {
if (threadCount < NUMBER_OF_THREADS) {
//if (false && threadCount < NUMBER_OF_THREADS) {
threadCount += 2;
thread t1(&Render::renderSilverRect, this, imin, imax, jmin, j,
sameTop, iterTop, sameNewLine, iterNewLine, sameLeftTop, iterLeftTop, sameRightTop, iterRightTop);
thread t2(&Render::renderSilverRect, this, imin, imax, j, jmax,
sameNewLine, iterNewLine, sameBottom, iterBottom, sameLeftBottom, iterLeftBottom, sameRightBottom, iterRightBottom);
t1.join(); threadCount--;
t2.join(); threadCount--;
}
else {
if (height - jmax < 0.5*height) {
renderSilverRect(imin, imax, jmin, j,
sameTop, iterTop, sameNewLine, iterNewLine, sameLeftTop, iterLeftTop, sameRightTop, iterRightTop);
renderSilverRect(imin, imax, j, jmax,
sameNewLine, iterNewLine, sameBottom, iterBottom, sameLeftBottom, iterLeftBottom, sameRightBottom, iterRightBottom);
}
else {
//same in different order
renderSilverRect(imin, imax, j, jmax,
sameNewLine, iterNewLine, sameBottom, iterBottom, sameLeftBottom, iterLeftBottom, sameRightBottom, iterRightBottom);
renderSilverRect(imin, imax, jmin, j,
sameTop, iterTop, sameNewLine, iterNewLine, sameLeftTop, iterLeftTop, sameRightTop, iterRightTop);
}

}
}
else {
bool sameNewLine = true;
int i = imin + (imax - imin) / 2;
//Compute new line
int iterNewLine = mandelIterate(i, jmin + 1);
for (int k = jmin + 2; k < jmax; k++) {
if (mandelIterate(i, k) != iterNewLine) {
sameNewLine = false;
}
}
//Check Top and Bottom for equality
bool sameRightTop = true;
bool sameLeftTop = true;
bool sameLeftBottom = true;
bool sameRightBottom = true;
int iterRightTop = getPixel(i, jmin);// fractalCanvas[i*height + jmin];
int iterLeftTop = getPixel(imin, jmin);// fractalCanvas[imin*height + jmin];
int iterRightBottom = getPixel(i, jmax);// fractalCanvas[i*height + jmax];
int iterLeftBottom = getPixel(imin, jmax);// fractalCanvas[imin*height + jmax];
if (!sameTop) {
for (int k = i + 1; k < imax; k++) {
//if (fractalCanvas[k*height + jmin] != iterRightTop) {
if (getPixel(k, jmin) != iterRightTop) {
sameRightTop = false;
break;
}
}
for (int k = imin + 1; k < i; k++) {
if (getPixel(k, jmin) != iterLeftTop) {
sameLeftTop = false;
break;
}
}
}
if (!sameBottom) {
for (int k = i + 1; k < imax; k++) {
//if (fractalCanvas[k*height + jmax] != iterRightBottom) {
if (getPixel(k, jmax) != iterRightBottom) {
sameRightBottom = false;
break;
//hier gebeurt het
}
}
for (int k = imin + 1; k < i; k++) {
if (getPixel(k, jmax) != iterLeftBottom) {
sameLeftBottom = false;
break;
}
}
}
//if (true || threadCount < NUMBER_OF_THREADS) {
if (threadCount < NUMBER_OF_THREADS) {
//if (false && threadCount < NUMBER_OF_THREADS) {
threadCount += 2;
thread t1(&Render::renderSilverRect, this, imin, i, jmin, jmax,
sameLeftTop, iterLeftTop, sameLeftBottom, iterLeftBottom, sameLeft, iterLeft, sameNewLine, iterNewLine);
thread t2(&Render::renderSilverRect, this, i, imax, jmin, jmax,
sameRightTop, iterRightTop, sameRightBottom, iterRightBottom, sameNewLine, iterNewLine, sameRight, iterRight);
t1.join(); threadCount--;
t2.join(); threadCount--;
}
else {
if (width - imax < 0.5*width) {
renderSilverRect(imin, i, jmin, jmax,
sameLeftTop, iterLeftTop, sameLeftBottom, iterLeftBottom, sameLeft, iterLeft, sameNewLine, iterNewLine);
renderSilverRect(i, imax, jmin, jmax,
sameRightTop, iterRightTop, sameRightBottom, iterRightBottom, sameNewLine, iterNewLine, sameRight, iterRight);
}
else {
renderSilverRect(i, imax, jmin, jmax,
sameRightTop, iterRightTop, sameRightBottom, iterRightBottom, sameNewLine, iterNewLine, sameRight, iterRight);
renderSilverRect(imin, i, jmin, jmax,
sameLeftTop, iterLeftTop, sameLeftBottom, iterLeftBottom, sameLeft, iterLeft, sameNewLine, iterNewLine);
}
}
}
pixelGroupings += 2;
}


bool Render::calcHorizontalLine(int iFrom, int iTo, int height) {
if(iTo < iFrom) cout << "GAAT HELEMAAL FOUT";
bool thisSame = true;
int thisIter = mandelIterate(iFrom, height);
for (int i = iFrom + 1; i < iTo; i++) {
if (thisIter != mandelIterate(i, height))
thisSame = false;
}
return thisSame;
}

bool Render::calcVerticalLine(int jFrom, int jTo, int width) {
if(jTo < jFrom) cout << "GAAT HELEMAAL FOUT";
bool thisSame = true;
int thisIter = mandelIterate(width, jFrom);
for (int j = jFrom + 1; j < jTo; j++) {
if (thisIter != mandelIterate(width, j))
thisSame = false;
}
return thisSame;
}

void Render::renderSilverFull() {
//This calculates a raster of pixels first and then launches threads for each tile in the raster.
//Use the option "View guessed pixels" in the program while in a sparse area to see how it works.
int imin = 0;
int imax = width - 1;
int jmin = 0;
int jmax = height - 1;

double tileGridSize = sqrt(NUMBER_OF_THREADS);
int floor = (int)tileGridSize;

int widthStep = width / floor;
int heightStep = height / floor;

vector<bool> isSameList(2 * floor*(floor + 1));
vector<int> heights(floor + 1);
vector<int> widths(floor + 1);
/*
Render* renders = (Render*)malloc(5 * sizeof(Render));
renders[0] = Render(500);
renders[0].threadCount += 101;
for (int i = 0; i < 5; i++)
cout << renders[i].threadCount << " is de threadCount van de render " << i << endl;

*/
for (int k = 0; k < floor; k++) {
heights[k] = k * heightStep;
widths[k] = k * widthStep;
}
heights[floor] = jmax;
widths[floor] = imax;

int isSameListIndex = 0;
for (int lineNumH = 0; lineNumH < floor; lineNumH++) {
for (int lineNumV = 0; lineNumV < floor; lineNumV++) {
isSameList[(lineNumH * floor + lineNumV) * 2] = calcHorizontalLine(widths[lineNumH], widths[lineNumH + 1], heights[lineNumV]);
isSameList[(lineNumH * floor + lineNumV) * 2 + 1] = calcVerticalLine(heights[lineNumV], heights[lineNumV + 1], widths[lineNumH]);
cout << "still workin with linenumH: " << lineNumH << "  and lineNumV: " << lineNumV << endl;
}
}
for (int lineNumH = 0; lineNumH < floor; lineNumH++) {
isSameList[(lineNumH * floor + floor) * 2] = calcHorizontalLine(widths[lineNumH], widths[lineNumH + 1], jmax);
}
for (int lineNumV = 0; lineNumV < floor; lineNumV++) {
isSameList[(floor * floor + lineNumV) * 2 + 1] = calcVerticalLine(heights[lineNumV], heights[lineNumV + 1], imax);
}
mandelIterate(imax, jmax);

vector<thread> threads(floor*floor);
//thread* threads = (thread*)calloc(sizeof(thread), floor*floor);
int createdTileThreads = 0;
threadCount = floor * floor;

for (int lineNumH = 0; lineNumH < floor; lineNumH++) {
for (int lineNumV = 0; lineNumV < floor; lineNumV++) {
int thisImin = widths[lineNumH];
int thisImax = widths[lineNumH + 1];
int thisJmin = heights[lineNumV];
int thisJmax = heights[lineNumV + 1];

int iterTop = getPixel(thisImax, thisJmin);
int iterBottom = getPixel(thisImax, thisJmax);
int iterLeft = getPixel(thisImin, thisJmax);
int iterRight = getPixel(thisImax, thisJmax);

bool sameTop = isSameList[(lineNumH * floor + lineNumV) * 2];
bool sameBottom = isSameList[(lineNumH * floor + (lineNumV + 1)) * 2];
bool sameLeft = isSameList[(lineNumH * floor + lineNumV) * 2 + 1];
bool sameRight = isSameList[((lineNumH + 1) * floor + lineNumV) * 2 + 1];

cout << "creating thread " << createdTileThreads << endl;
threads[createdTileThreads++] = thread(&Render::renderSilverRect, this, thisImin, thisImax, thisJmin, thisJmax,
sameTop, iterTop, sameBottom, iterBottom, sameLeft, iterLeft, sameRight, iterRight);
cout << "created" << endl;
}
}

cout << "joining " << createdTileThreads << " threads" << endl;
for (int i = 0; i < createdTileThreads; i++) {
threads[i].join();
}
cout << "threads joined" << endl;
return;
}

void Render::start() {
threadCount = 0;
pixelGroupings = 0;
renderSilverFull();
}

void refreshDuringRender(int* instance, int renderID) {
HDC hdc = GetDC(hWndMain);

this_thread::sleep_for(std::chrono::milliseconds(70));
while (instance[0] == renderID) {
cout << "drawing" << endl;
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
this_thread::sleep_for(std::chrono::milliseconds(100));
}
free(instance);

cout << "last drawing" << endl;
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
ReleaseDC(hWndMain, hdc);
firstPaint = false;
}

void multithreadRender(HWND hWnd) {
//this function controls the whole rendering process from start to finish. despite the name it's not necessarily multithreaded

int renderID = ++lastRenderID;
int* renderInstance = (int*)malloc(sizeof(int));
renderInstance[0] = renderID;
thread t(refreshDuringRender, renderInstance, renderID);
t.detach();

printf("-----New Render-----\n"
"renderID: %d\n"
"center: %f + %fi\n", renderID, real(center), imag(center));
cout << "xrange: " << x_range << "\n";
cout << "yrange: " << y_range << "\n";
//hWndMain = hWnd;
int pixelGroupings = 0;
int guessedPixels = 0;

auto start = chrono::high_resolution_clock::now();
if (formulaType != FORMULA_BURNING_SHIP && formulaType != FORMULA_CHECKERS && formulaType != FORMULA_TEST_CONTROL) {
//This is for all renders that have a Mandelbrot type formula (z->f(z, c)) and that allow for guessing algorithms to be used
Render renderTask(renderID);
renderTask.start();
printf("guessedPixelCount: %d / %d\n", renderTask.guessedPixelCount, width*height);
printf("pixelGroupings: %d\n", renderTask.pixelGroupings);
pixelGroupings = renderTask.pixelGroupings;
guessedPixels = renderTask.guessedPixelCount;
}
else {
//This is for renders that have a different type of procedure.
{
int j = 0;
int i = 0;
for (i = 0; i < width; i++) {
for (j = 0; j < height; j++) {
double_c c;
double_c z;
int iterationCount = 0;
if (formulaType == FORMULA_CHECKERS) {
double_c c = transformation(inflections(map(i, j)));
//create checkerboard tiles
double resolution = 3.1415926535897932384626433832795; //tile size, pi goes well with the natural log transformation
bool vertical = (int)(imag(c) / resolution) % 2 == 0;
bool horizontal = (int)(real(c) / resolution) % 2 == 0;
bool result = horizontal ^ vertical;
if ((imag(c) < 0) ^ (real(c) < 0)) result = !result;

//create julia detail simulation
double transRe = real(c) - (int)(real(c) / resolution) * resolution; //coords translated to the first quadrant's first tile for simplicity
double transIm = imag(c) - (int)(imag(c) / resolution) * resolution;
if (transRe < 0) transRe += resolution;
if (transIm < 0) transIm += resolution;

bool underInc = transIm < transRe; //coordinate is under the increasing line from (0,0) to (resolution, resolution)
bool underDec = transIm < resolution - transRe; //under decreasing line from (0, resolution) to (resolution, 0)

double_c ref;
if (underInc) {
if (underDec) ref = 0.5*resolution;
else ref = resolution + 0.5*resolution*I;
}
else {
if (underDec) ref = 0.5*resolution*I;
else ref = 0.5*resolution + resolution * I;
}

double transRefDist = sqrt(pow(transRe - real(ref), 2) + pow(transIm - imag(ref), 2));
double distLog = log(transRefDist);
int resFactors = (int)(distLog / resolution - 0.5);

if (resFactors % 2 != 0) result = !result;

if (result) iterationCount = 500;
else iterationCount = 0;
}
else if (formulaType == FORMULA_BURNING_SHIP) {
if (julia) {
c = juliaSeed;
z = transformation(inflections(map(i, j)));
}
else {
c = transformation(inflections(map(i, j)));
z = 0;
}
if (renderID == lastRenderID) {
while (real(z)*real(z) + imag(z)*imag(z) < 4 && iterationCount < maxIters) {
z = formula(z, c);
iterationCount++;
}
}
}
else { //FORMULA_TEST_CONTROL

double_c c = transformation(inflections(map(i, j)));
double cr;
double ci;
double zr;
double zi;
double zrsqr;
double zisqr;
cr = real(c);
ci = imag(c);
zr = 0;
zi = 0;
zrsqr = 0;
zisqr = 0;
while (zrsqr + zisqr <= 4.0 && iterationCount < maxIters) {
zi = zr * zi;
zi += zi;
zi += ci;
zr = zrsqr - zisqr + cr;
zrsqr = zr * zr;
zisqr = zi * zi;
iterationCount++;
}
}
fractalCanvas[i*height + j].iterationCount = iterationCount;
ptPixels[width*(height - j - 1) + i] = gradient(iterationCount);
//setPixel(i, j, iterationCount, ISCALCULATED);
}
}
}
}

renderInstance[0] = -1;

auto finish = chrono::high_resolution_clock::now();
chrono::duration<double> elapsed = finish - start;
cout << "Elapsed time: " << elapsed.count() << " s\n";

long long computedIterations = 0;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
computedIterations += fractalCanvas[i*height + j].iterationCount;
}
}
cout << "computed iterations: " << computedIterations << endl;
cout << "iterations per second: " << ((long long)(computedIterations / elapsed.count()) / 1000000.0) << " M" << endl;
cout << "pixel groupings per second: " << pixelGroupings / elapsed.count() << endl;
cout << "guessed pixels per second: " << guessedPixels / elapsed.count() << endl;
cout << "guessed pixels per grouping: " << (double)guessedPixels / pixelGroupings << endl;
cout << "guessed pixels per grouping time: " << ((double)guessedPixels / pixelGroupings)*elapsed.count() << endl;
cout << "pixel groupings per (iterations per second (millions)) " << pixelGroupings / ((long long)(computedIterations / elapsed.count()) / 1000000.0) << endl;
return;
}

void recalculate() {
thread t(multithreadRender, hWndMain);
t.detach();
SendMessageW(hOptions, CUSTOM_REFRESH, 0, 0);
}

bool resize(int newWidth, int newHeight, int newScreenWidth, int newScreenHeight) {
if (newWidth == width && newHeight == height) {
cout << "entered resize, newWidth=width and newHeight=height. Nothing happens." << endl;
return false;
}
else {
if (
!(newWidth > 0
&& newWidth < MAXRES_WIDTH
&& newHeight > 0
&& newHeight < MAXRES_HEIGHT)
) {
cout << "Width or height outside of allowed range." << endl;
cout << "width: " << newWidth << "  " << "height: " << newHeight << endl;
cout << "maximum allowed: width: " << MAXRES_WIDTH << "  " << "height: " << MAXRES_HEIGHT << endl;
return false;
}
}

cout << "resize happens" << endl;

width = newWidth;
height = newHeight;
screenWidth = newScreenWidth;
screenHeight = newScreenHeight;
updateCenterAndZoom();
//y_range = x_range * ((double)height / (double)width);

cout << "before freeing memory" << endl;
DeleteObject(screenBMP);
free(fractalCanvas);
fractalCanvas = (iterData*)malloc(width*height * sizeof(iterData));
cout << "realloced fractalcanvas" << endl;

HDC hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);
BITMAPINFO RGB32BitsBITMAPINFO;
ZeroMemory(&RGB32BitsBITMAPINFO, sizeof(BITMAPINFO));
RGB32BitsBITMAPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
RGB32BitsBITMAPINFO.bmiHeader.biWidth = width;
RGB32BitsBITMAPINFO.bmiHeader.biHeight = height;
RGB32BitsBITMAPINFO.bmiHeader.biPlanes = 1;
RGB32BitsBITMAPINFO.bmiHeader.biBitCount = 32;
screenBMP = CreateDIBSection(
hdc,
(BITMAPINFO *)&RGB32BitsBITMAPINFO,
DIB_RGB_COLORS,
(void**)&ptPixels,
NULL, 0
);
cout << "to recaclc" << endl;
SetWindowPos(hWndMain, HWND_TOP, 0, 0, screenWidth + 20, screenHeight + 70, SWP_NOMOVE);
InvalidateRect(hWndMain, NULL, TRUE);
return true;
}

UINT tests();

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message)
{
case WM_CREATE: {
hWndMain = hWnd;
NUMBER_OF_THREADS = thread::hardware_concurrency() + 4;
printf("number of threads: %d\n", NUMBER_OF_THREADS);
if (NUMBER_OF_THREADS == 0) {
printf("Couldn't detect the number of cores (default: 12)\n");
NUMBER_OF_THREADS = 12;
}
cout << setprecision(21); //precision when printing doubles
tests();
AddMenus(hWnd);
setCenterAndZoom(0, 0);
/*
{//Test location:
x_range = 0.000000476837158203125;
setCenterAndZoom(-0.1051904975 + 0.9269252546*I, getZoomLevel());
}
*/
resize(width + 1, height + 1, width + 1, height + 1);
recalculate();
break;
}
case WM_COMMAND: {
//menu selection
bool fractalTypeChange = true;
switch (LOWORD(wParam)) {//fractal types
case FORMULA_BURNING_SHIP:
case FORMULA_M_2:
inflectionPower = 2;
bailout = 4;
break;
case FORMULA_M_3:
inflectionPower = 3;
bailout = 2;
break;
case FORMULA_M_4:
inflectionPower = 4;
bailout = pow(2, 2 / 3.);
break;
case FORMULA_M_5:
inflectionPower = 5;
bailout = pow(2, 2 / 4.); //case power of n: pow(2, 2/(n-1))
break;
case FORMULA_TEST:
break;
case FORMULA_TEST_CONTROL:
break;
case FORMULA_CHECKERS:
break;
case FORMULA_HIGH_POWER:
break;
default:
fractalTypeChange = false;
}
if (fractalTypeChange) {
formulaType = wParam;
setCenterAndZoom(0, 0);
recalculate();
break;
}
switch (LOWORD(wParam)) {//other menu buttons
case RESET:
setCenterAndZoom(0, 0);
recalculate();
break;
case TOGGLE_JULIA:
julia = !julia;
if (julia) {
juliaSeed = center;
setCenterAndZoom(0, 0);
}
else {
setCenterAndZoom(juliaSeed, 0);
}
recalculate();
break;
case CHANGE_TRANSFORMATION:
transformation_type = (transformation_type + 1) % NUMBER_OF_TRANSFORMATIONS;
setCenterAndZoom(0, 0);
recalculate();
break;
case WINDOW_OPTIONS:
//SendMessage(hOptions, WM_DESTROY, NULL, NULL); //destroy currently active options window? doesn't work
createOptionsWindow();
break;
case VIEW_GUESSED_PIXELS: {
refreshBitmap(true);
break;
}
case QUIT:
PostQuitMessage(0);
break;
}
break;
}
case WM_PAINT: {
if (firstPaint) break;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOWTEXT + 1));
DrawBitmap(hdc, 0, 0, screenBMP, SRCCOPY);
EndPaint(hWnd, &ps);
break;
}
case WM_MOUSEWHEEL: {
//zoom action
POINT point = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(hWnd, &point);
int xPos = point.x * oversampling;
int yPos = point.y * oversampling;
if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
return 0;
}

//set new parameters
//center = (map(xPos, yPos)+(map(xPos, yPos)+center)/2)/2; //working formula, albeit can be simplified
//the way this works is applying the known transformation for a zoom size of 2, 2 times.
//for the other direction: 2*(2*center-map(xPos, yPos))-map(xPos, yPos) is two times the inverse transformation
//center = map(xPos, yPos); //old method
int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
if (zDelta > 0) {
setCenterAndZoom(
0.75*(map(xPos, yPos)) + 0.25*center,
getZoomLevel() + 2
);
}
else {
setCenterAndZoom(
4.*center - 3.*(map(xPos, yPos)),
getZoomLevel() - 2
);
}

//generate preview bitmap
BITMAP Bitmap;
HDC screenHDC = CreateCompatibleDC(NULL);
GetObject(screenBMP, sizeof(BITMAP), (LPSTR)&Bitmap);
SelectObject(screenHDC, screenBMP);

if (zDelta > 0) {
StretchBlt(screenHDC, 0, 0, width, height, screenHDC, xPos - xPos / 4, yPos - yPos / 4, width / 4, height / 4, SRCCOPY); //stretch the screen bitmap
}
else {
SetStretchBltMode(screenHDC, HALFTONE);
StretchBlt(screenHDC, xPos - xPos / 4, yPos - yPos / 4, width / 4, height / 4, screenHDC, 0, 0, width, height, SRCCOPY);
}
DeleteDC(screenHDC);

recalculate();
break;
}
case WM_LBUTTONUP: {
//create inflection
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
return 0;
}
if (inflectionCount < MAX_NUMBER_OF_INFLECTIONS) {
inflectionCoords[inflectionCount] = map(xPos, yPos);
inflectionCount += 1;
setCenterAndZoom(0, 0);
/*
center = 0. + 0.*I;
x_range = 4;
y_range = x_range * ((double)height / (double)width);
topleftCorner = center - x_range / 2 + (y_range / 2)*I;
*/
printf("inflection coords:\n");
for (int i = 0; i < inflectionCount; i++) {
printf("i=%d", i); printf(": %f ", real(inflectionCoords[i])); printf("+ %f * I\n", imag(inflectionCoords[i]));
}
recalculate();
}
else {
MessageBoxA(NULL, "No more inflections can be set.", "Problem", MB_OK);
}
break;
}
case WM_RBUTTONUP: {
//remove inflection
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
return 0;
}
if (inflectionCount > 0) {
inflectionCount--;
recalculate();
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_ERASEBKGND: {
return true;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}

HWND optionsElements[30];
HWND& optionsElt(int index) {
return optionsElements[index - 100];
}
//identifiers start at 100:
const int optionWidth = 100;
const int optionHeight = 101;
const int optionOk = 102;
const int optionApply = 103;
const int optionCancel = 104;
const int optionHeightText = 105;
const int optionWidthText = 106;
const int optionGradientSpeed = 107;
const int optionGradientSpeedText = 108;
const int optionGradientSpeedTrackbarCoarse = 109;
const int optionGradientSpeedTrackbarFine = 110;
const int optionGradientSpeedTextCoarse = 111;
const int optionGradientSpeedTextFine = 112;
const int optionZoomLevelText = 113;
const int optionZoomLevel = 114;
const int optionMaxItersText = 115;
const int optionMaxIters = 116;
const int optionsOversamplingText = 117;
const int optionsRadioAA_1 = 118;
const int optionsRadioAA_2 = 119;
const int optionsRadioAA_3 = 120;
const int optionsRadioAA_4 = 121;
const int optionsRadioAA_6 = 122;
const int optionsRadioAA_8 = 123;
const int optionsRadioAA_12 = 124;
const int optionsRadioAA_16 = 125;
const int optionGradientOffsetText = 126;
const int optionGradientOffsetTrackbar = 127;

int getOverSampleIdentifier() {
switch (oversampling) {
case 1:
return optionsRadioAA_1;
case 2:
return optionsRadioAA_2;
case 3:
return optionsRadioAA_3;
case 4:
return optionsRadioAA_4;
case 6:
return optionsRadioAA_6;
case 8:
return optionsRadioAA_8;
case 12:
return optionsRadioAA_12;
case 16:
return optionsRadioAA_16;
}
return 0;
}

LRESULT CALLBACK OptionsProc(HWND hOptions, UINT message, WPARAM wParam, LPARAM lParam) {

switch (message)
{
case WM_CREATE: {
//Create all items: buttons, text fields...
optionsElt(optionWidthText) = CreateWindowExA(0, "STATIC", "Width:", WS_VISIBLE | WS_CHILD, 0, 0, 100, 30, hOptions, (HMENU)optionWidthText, hInst, NULL);
optionsElt(optionWidth) = CreateWindowExA(0, "EDIT", "", WS_VISIBLE | WS_CHILD, 60, 0, 100, 30, hOptions, (HMENU)optionWidth, hInst, NULL);
optionsElt(optionHeightText) = CreateWindowExA(0, "STATIC", "Height:", WS_VISIBLE | WS_CHILD, 170, 0, 100, 30, hOptions, (HMENU)optionHeightText, hInst, NULL);
optionsElt(optionHeight) = CreateWindowExA(0, "EDIT", "", WS_VISIBLE | WS_CHILD, 240, 0, 100, 30, hOptions, (HMENU)optionHeight, hInst, NULL);

optionsElt(optionsOversamplingText) = CreateWindowExA(0, "STATIC", "Oversampling:", WS_VISIBLE | WS_CHILD, 480, 10, 120, 30, hOptions, (HMENU)optionsOversamplingText, hInst, NULL);
optionsElt(optionsRadioAA_1) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "1x1", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON | WS_GROUP, 480, 30, 80, 20, hOptions, (HMENU)optionsRadioAA_1, hInst, NULL);
optionsElt(optionsRadioAA_2) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "2x2", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 480, 60, 80, 20, hOptions, (HMENU)optionsRadioAA_2, hInst, NULL);
optionsElt(optionsRadioAA_3) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "3x3", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 480, 90, 80, 20, hOptions, (HMENU)optionsRadioAA_3, hInst, NULL);
optionsElt(optionsRadioAA_4) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "4x4", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 480, 120, 80, 20, hOptions, (HMENU)optionsRadioAA_4, hInst, NULL);
optionsElt(optionsRadioAA_6) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "6x6", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 480, 150, 80, 20, hOptions, (HMENU)optionsRadioAA_6, hInst, NULL);
optionsElt(optionsRadioAA_8) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "8x8", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 480, 180, 80, 20, hOptions, (HMENU)optionsRadioAA_8, hInst, NULL);
optionsElt(optionsRadioAA_12) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "12x12", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 480, 210, 80, 20, hOptions, (HMENU)optionsRadioAA_12, hInst, NULL);
optionsElt(optionsRadioAA_16) = CreateWindowExA(WS_EX_WINDOWEDGE, "BUTTON", "16x16", WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 480, 240, 80, 20, hOptions, (HMENU)optionsRadioAA_16, hInst, NULL);

optionsElt(optionGradientSpeedText) = CreateWindowExA(0, "STATIC", "Gradient speed:", WS_VISIBLE | WS_CHILD, 0, 40, 130, 30, hOptions, (HMENU)optionGradientSpeedText, hInst, NULL);
optionsElt(optionGradientSpeed) = CreateWindowExA(0, "EDIT", "", WS_VISIBLE | WS_CHILD, 130, 40, 150, 30, hOptions, (HMENU)optionGradientSpeed, hInst, NULL);

optionsElt(optionGradientSpeedTextCoarse) = CreateWindowExA(0, "STATIC", "Coarse", WS_VISIBLE | WS_CHILD, 0, 85, 60, 30, hOptions, (HMENU)optionGradientSpeedTextCoarse, hInst, NULL);
optionsElt(optionGradientSpeedTrackbarCoarse) = CreateTrackbar(hOptions, 0, 100, 60, 85, 400, 30, optionGradientSpeedTrackbarCoarse);
optionsElt(optionGradientSpeedTextFine) = CreateWindowExA(0, "STATIC", "Fine", WS_VISIBLE | WS_CHILD, 0, 115, 110, 30, hOptions, (HMENU)optionGradientSpeedTextFine, hInst, NULL);
optionsElt(optionGradientSpeedTrackbarFine) = CreateTrackbar(hOptions, 0, 100, 60, 115, 400, 30, optionGradientSpeedTrackbarFine);
optionsElt(optionGradientOffsetText) = CreateWindowExA(0, "STATIC", "Offset", WS_VISIBLE | WS_CHILD, 0, 145, 130, 30, hOptions, (HMENU)optionGradientOffsetText, hInst, NULL);
optionsElt(optionGradientOffsetTrackbar) = CreateTrackbar(hOptions, 0, 100, 60, 145, 400, 30, optionGradientOffsetTrackbar);
SendMessage(optionsElt(optionGradientSpeedTrackbarCoarse), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)((int)gradientSpeed));
SendMessage(optionsElt(optionGradientSpeedTrackbarFine), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(100 * (gradientSpeed - (int)gradientSpeed)));
SendMessage(optionsElt(optionGradientOffsetTrackbar), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(100 * gradientOffset));

optionsElt(optionZoomLevelText) = CreateWindowExA(0, "STATIC", "Zoom:", WS_VISIBLE | WS_CHILD, 0, 200, 60, 30, hOptions, (HMENU)optionZoomLevelText, hInst, NULL);
optionsElt(optionZoomLevel) = CreateWindowExA(0, "EDIT", "", WS_VISIBLE | WS_CHILD, 60, 200, 150, 30, hOptions, (HMENU)optionZoomLevel, hInst, NULL);

optionsElt(optionMaxItersText) = CreateWindowExA(0, "STATIC", "Max iterations:", WS_VISIBLE | WS_CHILD, 0, 235, 130, 30, hOptions, (HMENU)optionMaxItersText, hInst, NULL);
optionsElt(optionMaxIters) = CreateWindowExA(0, "EDIT", "", WS_VISIBLE | WS_CHILD, 130, 235, 100, 30, hOptions, (HMENU)optionMaxIters, hInst, NULL);

optionsElt(optionOk) = CreateWindowExA(0, "BUTTON", "Ok", WS_CHILD | WS_VISIBLE, 0, 400, 60, 30, hOptions, (HMENU)optionOk, hInst, NULL);
optionsElt(optionApply) = CreateWindowExA(0, "BUTTON", "Apply", WS_CHILD | WS_VISIBLE, 80, 400, 60, 30, hOptions, (HMENU)optionApply, hInst, NULL);
optionsElt(optionCancel) = CreateWindowExA(0, "BUTTON", "Cancel", WS_CHILD | WS_VISIBLE, 160, 400, 60, 30, hOptions, (HMENU)optionCancel, hInst, NULL);

ShowWindow(hOptions, SW_SHOW);
//break intentionally left out
}
case CUSTOM_REFRESH: {
cout << "Refresh options" << endl;
//set oversampling
if (oversampling != height / screenHeight)
cout << "Warning: screen width and height don't match: widthxheight vs screenwidthxscreenheight:   " << width << "x" << height << " vs " << screenWidth << "x" << screenHeight << endl;
SendMessage(optionsElt(getOverSampleIdentifier()), BM_SETCHECK, BST_CHECKED, 0);

//set gradient speed
SetDlgItemTextA(hOptions, optionGradientSpeed, to_string(gradientSpeed).c_str());

//set width, height
SetDlgItemTextA(hOptions, optionHeight, to_string(screenHeight).c_str());
SetDlgItemTextA(hOptions, optionWidth, to_string(screenWidth).c_str());

//Set zoom level
SetDlgItemTextA(hOptions, optionZoomLevel, to_string(getZoomLevel()).c_str());

//Set max iters
SetDlgItemTextA(hOptions, optionMaxIters, to_string(maxIters).c_str());
break;
}
case WM_HSCROLL: {
SendMessage(hOptions, CUSTOM_REFRESH, 0, 0);
bool refreshBitmapRequired = false;
if (lParam == (LPARAM)optionsElt(optionGradientSpeedTrackbarCoarse) || lParam == (LPARAM)optionsElt(optionGradientSpeedTrackbarFine) ) {
int posCoarse = SendMessage(optionsElt(optionGradientSpeedTrackbarCoarse), TBM_GETPOS, 0, 0);
int posFine = SendMessage(optionsElt(optionGradientSpeedTrackbarFine), TBM_GETPOS, 0, 0);
double newGradientSpeed = posCoarse + 0.01*(double)posFine;
refreshBitmapRequired = setGradientSpeed(newGradientSpeed);
}
else if (lParam == (LPARAM)optionsElt(optionGradientOffsetTrackbar)) {
double newGradientOffset = 0.01 * SendMessage(optionsElt(optionGradientOffsetTrackbar), TBM_GETPOS, 0, 0);
cout << "new gradientOffset: " << newGradientOffset << endl;
refreshBitmapRequired = setGradientOffset(newGradientOffset);
}
if (refreshBitmapRequired)
refreshBitmap(false);
break;
}
case WM_COMMAND: {
if (wParam == optionApply || wParam == optionOk) {
bool recalcNeeded = false;

//change iteration count:
char aMaxIters[16]; GetDlgItemTextA(hOptions, optionMaxIters, aMaxIters, sizeof(aMaxIters));
int newMaxIters = atoi(aMaxIters);
recalcNeeded |= setMaxIters(newMaxIters);

//change gradient speed:
char aGradientSpeed[16]; GetDlgItemTextA(hOptions, optionGradientSpeed, aGradientSpeed, sizeof(aGradientSpeed));
double newGradientSpeed = strtod(aGradientSpeed, NULL);
setGradientSpeed(newGradientSpeed);
refreshBitmap(false);

//change zoom level:
char aZoomLevel[16]; GetDlgItemTextA(hOptions, optionZoomLevel, aZoomLevel, sizeof(aZoomLevel));
double newZoomLevel = strtod(aZoomLevel, NULL);
recalcNeeded |= setCenterAndZoom(center, newZoomLevel);

//resize:
char aWidth[16]; GetDlgItemTextA(hOptions, optionWidth, aWidth, sizeof(aWidth));
char aHeight[16]; GetDlgItemTextA(hOptions, optionHeight, aHeight, sizeof(aHeight));
int newScreenWidth = atoi(aWidth);
int newScreenHeight = atoi(aHeight);

//change oversampling
if (IsDlgButtonChecked(hOptions, optionsRadioAA_1) == BST_CHECKED) oversampling = 1;
else if (IsDlgButtonChecked(hOptions, optionsRadioAA_2) == BST_CHECKED) oversampling = 2;
else if (IsDlgButtonChecked(hOptions, optionsRadioAA_3) == BST_CHECKED) oversampling = 3;
else if (IsDlgButtonChecked(hOptions, optionsRadioAA_4) == BST_CHECKED) oversampling = 4;
else if (IsDlgButtonChecked(hOptions, optionsRadioAA_6) == BST_CHECKED) oversampling = 6;
else if (IsDlgButtonChecked(hOptions, optionsRadioAA_8) == BST_CHECKED) oversampling = 8;
else if (IsDlgButtonChecked(hOptions, optionsRadioAA_12) == BST_CHECKED) oversampling = 12;
else if (IsDlgButtonChecked(hOptions, optionsRadioAA_16) == BST_CHECKED) oversampling = 16;

recalcNeeded |= resize(newScreenWidth*oversampling, newScreenHeight*oversampling, newScreenWidth, newScreenHeight);

if (recalcNeeded) {
recalculate();
}
}
if (wParam == optionOk || wParam == optionCancel) {
DestroyWindow(hOptions);
}
cout << "wParam is: " << wParam << endl;
break;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hOptions, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW));
EndPaint(hOptions, &ps);
break;
}
case WM_DESTROY: {
break;
}
case WM_ERASEBKGND: {
return true;
}
default:
return DefWindowProc(hOptions, message, wParam, lParam);
break;
}
return 0;
}

UINT tests() {
/*
cout << sizeof(long double) << endl;
cout << sizeof(double) << endl;
double_c z = -0.249948099245828725401 + -1.33376245206345434013*I;
cout << "testformule" << pow(z, 2)*pow((pow(z, 2) + 1.0), 3) << endl;
*/

/*
//<gmp test>
mpz_t t;
mpz_init(t);
mpz_set_ui(t, 15000);
gmp_printf("%Zd\n", t);
//</gmp test>
mpf_t f, g;
cout << mpf_get_default_prec() << endl;
//mpf_set_default_prec(100)
mpf_init(f); mpf_init(g);
mpf_set_d(f, 100); mpf_set_d(g, 101);
*/
/*
mpf_out_str(stdout, 10, 100, f); printf(" "); mpf_out_str(stdout, 10, 100, g); printf("\n");
*/

/*
for (int i = 0; i < 5; i++) {
cout << random() << endl;
}
cout << "log 10: " << log(10) << endl;
cout << "log10 10: " << log10(10) << endl;
*/

/* //general timer lines to put something between:
auto start = chrono::high_resolution_clock::now();
auto finish = chrono::high_resolution_clock::now();
chrono::duration<double> elapsed = finish - start;
*/

UINT result = 1;
/*
auto start = chrono::high_resolution_clock::now();
for (int i = 0; i < 100100100; i++) {
result = gradient(i);
}
auto finish = chrono::high_resolution_clock::now();
chrono::duration<double> elapsed = finish - start;
cout << "gradient took: " << elapsed.count() << endl;
*/

/*
#define TEST(T) \
  do { T x = 1, y = 1; int n = 0; \
while (x + y != x) { y /= 2; n += 1; } \
printf("%s\t%d\n", #T, n); \
  } while(0)

printf("# type\tbits\n");
TEST(float);
TEST(double);
TEST(long double);
*/
return result;
}
« Last Edit: April 09, 2019, 11:36:57 PM by Dinkydau »

Offline gerson

  • Fractal Freak
  • **
  • Posts: 742
Re: Explore fractals (inflection tool)
« Reply #7 on: April 05, 2019, 04:09:23 PM »
Good news here. Liked a lot.
Are there a way to open/save parameters and save images?
"Reset" only reset the zoom. Are there some way to go to beggining (the start image) without runing program again?
Thanks for releasing.
« Last Edit: April 05, 2019, 06:24:31 PM by gerson »

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Re: Explore fractals (inflection tool)
« Reply #8 on: April 07, 2019, 09:45:55 PM »
I will try to make saving images and parameters. It's something I want as well.

I see the benefit of making reset reset some more things, but not many. I would usually want to reset the gradient, maybe iterations, maybe oversampling, but not the resolution and fractal type. If reset does less there's less risk of it changing something you want to keep.

Thanks for replying

Offline wbarry

  • Fractal Freshman
  • *
  • Posts: 9
Re: Explore fractals (inflection tool)
« Reply #9 on: May 16, 2019, 06:55:07 PM »
Hi Dinkydau,

I like your work. You have discovered some truly interesting areas of the set and you inspire me to dig deeper. I especially like the one where you found 3 different shapes/motifs in a single image.

I have been exploring the idea that we can anticipate what we will find, if we can understand the rules for how the set develops.
That there are a limited number of "moves" we can make (one of which is Bifurcation) when exploring.
I believe I may be starting to understand how it all works.
https://youtu.be/Oq1v3HzRoBE

I *think* that if we can discover the rules, you may be able to design your Julia Sets for morphing. Maybe.

Does this seem like anything new or useful? Did you already know about this?
I am asking you directly because your images prove you know your way around the MS.

Thanks! wbarry

Offline gerrit

  • 3f
  • ******
  • Posts: 2469

Offline claude

  • 3f
  • ******
  • Posts: 2033
    • mathr.co.uk

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Re: Explore fractals (inflection tool)
« Reply #12 on: May 20, 2019, 07:55:41 PM »
Hi Dinkydau,

I like your work. You have discovered some truly interesting areas of the set and you inspire me to dig deeper. I especially like the one where you found 3 different shapes/motifs in a single image.

I have been exploring the idea that we can anticipate what we will find, if we can understand the rules for how the set develops.
That there are a limited number of "moves" we can make (one of which is Bifurcation) when exploring.
I believe I may be starting to understand how it all works.
https://youtu.be/Oq1v3HzRoBE

I *think* that if we can discover the rules, you may be able to design your Julia Sets for morphing. Maybe.

Does this seem like anything new or useful? Did you already know about this?
I am asking you directly because your images prove you know your way around the MS.

Thanks! wbarry
Nice that you have discovered it

Designing julia morphings is precisely the purpose of this program. A click in the program simulates the effect of zooming what is usually around 1.5 times deeper at that location, so it can be used as a preview of what would happen if you zoomed there. You can design a julia morphing by using clicks and then proceed to zoom to it in the real Mandelbrot set.

The program can't help with the idea of repeatedly going "left" though. You need to tell it where to go. Claude knows tricks with external rays that can be used to find the location of specific zoom actions. For that I came up with the language idea that Claude linked to. Unfortunately I don't understand the math of external rays so I didn't get any further.

Offline Dinkydau

  • Uploader
  • *
  • Posts: 301
    • DeviantART gallery
Re: Explore fractals (inflection tool)
« Reply #13 on: July 27, 2019, 02:50:49 AM »
ExploreFractals_3.exe   537 KB
https://mega.nz/#!WxljHSTI!tH36TaUZgcA4CL2T1zkL960_VXVcYc-YuGt4X-7sx0M
CRC32: EB7EF562
MD5: 664D1AF5730CE2E26FB4A5018948CD4E

Changes:
1. You can save and load parameter files.
2. You can save the image as BMP.
3. The program uses the maximum number of threads more often by
3.1. keeping track of the number of active threads and creating a new thread with work that's left to do when a thread is done, and
3.2. calculating the boundaries of the first tiles with the maximum number of threads as well, instead of just 1.

Known problems:
1. I noticed the location of inflections is sometimes/always wrong when anti-aliasing is enabled.

Next time it will hopefully be able to use AVX instructions. It's almost done already but there's a bug I haven't been able to fix and I don't know how much longer it would take before I finally get to it again.

I tried to include the source code but "The message exceeds the maximum allowed length (80000 characters).". What's funny is that the limit was raised to this amount after I complained about not being able to post the coordinates for a render which were 25800 characters long. The code is a mess now anyway. I will try it again next time.
« Last Edit: July 27, 2019, 03:01:06 AM by Dinkydau »

Offline 3DickUlus

  • Administrator
  • *******
  • Posts: 2528
    • Digilantism
Re: Explore fractals (inflection tool)
« Reply #14 on: July 27, 2019, 04:12:15 AM »
Attach as zip ;)


xx
ExploreFractals (inflection tool)

Started by Dinkydau on Downloads

0 Replies
116 Views
Last post March 26, 2021, 11:43:09 PM
by Dinkydau
clip
Conversion tool for Lyapunov parameter files

Started by marcm200 on Programming

1 Replies
329 Views
Last post January 29, 2019, 01:45:59 PM
by gerson
clip
Inflector Gadget - inflection mapping for complex quadratic polynomials

Started by claude on Other

6 Replies
883 Views
Last post December 14, 2017, 06:13:37 PM
by claude
xx
Fractals Made of Fractals

Started by AranLeer on Fractal Mathematics And New Theories

11 Replies
1098 Views
Last post August 29, 2021, 10:26:27 PM
by 3DickUlus
wink
Hi Fractals!

Started by awfulTHEfrac on Meet & Greet

1 Replies
382 Views
Last post December 01, 2019, 10:43:56 AM
by 3DickUlus