/**
 **************************************************
 * @file        Inkplate6PLUS_Mandelbrot_Set.ino
 * @brief       Render the Mandelbrot set on Inkplate 6PLUS in 1-bit (BW) mode.
 *
 * @details     This example computes and renders the Mandelbrot set by iterating
 *              the complex recurrence z = z^2 + c for each screen pixel. Pixels
 *              that do not escape within MAXITERATIONS are drawn as black (inside
 *              the set); pixels that escape are drawn as white (outside).
 *
 *              Rendering is performed entirely on the MCU and is computationally
 *              intensive, so drawing a full frame can take minutes depending on
 *              the chosen view window and iteration limit. Progress is printed
 *              to the Serial Monitor once per rendered row.
 *
 *              The display runs in 1-bit (BW) mode (INKPLATE_1BIT) and uses a
 *              full-screen refresh (display()) after all pixels are computed.
 *
 * Requirements:
 * - Board:      Soldered Inkplate 6PLUS
 * - Hardware:   Inkplate 6PLUS, USB cable
 * - Extra:      none
 *
 * Configuration:
 * - Boards Manager -> Inkplate Boards -> Soldered Inkplate6PLUS
 * - Serial settings: 115200 baud (recommended to see row progress)
 * - Mandelbrot view window:
 *   - Adjust xFrom/xTo/yFrom/yTo to explore different regions
 * - Iterations:
 *   - MAXITERATIONS controls detail vs. render time
 *
 * Don't have Inkplate Boards in Arduino Boards Manager?
 * See https://docs.soldered.com/inkplate/6PLUS/quick-start-guide/
 *
 * How to use:
 * 1) Upload the sketch to Inkplate 6PLUS.
 * 2) Wait while the frame is computed (this can take a few minutes).
 * 3) After rendering completes, the image is shown on the display.
 * 4) The sketch waits briefly and then recomputes the same view again.
 *
 * Expected output:
 * - Display: A black/white Mandelbrot rendering for the configured coordinate
 *   window.
 * - Serial Monitor: The current row index (j) printed during rendering.
 *
 * Notes:
 * - Display mode is 1-bit (BW). This example performs a full refresh only after
 *   the entire image is computed.
 * - High MAXITERATIONS and wide/zoomed views increase render time significantly.
 * - This example does not use partial updates, touchscreen, WiFi, or deep sleep.
 *
 * Docs:         https://docs.soldered.com/inkplate
 * Support:      https://forum.soldered.com/
 *
 * @author      Soldered
 * @date        2024-03-15
 * @license     GNU GPL V3
 **************************************************/

// Next 3 lines are a precaution, you can ignore those, and the example would also work without them
#if !defined(ARDUINO_INKPLATE6PLUS) && !defined(ARDUINO_INKPLATE6PLUSV2)
#error "Wrong board selection for this example, please select e-radionica Inkplate 6Plus or Soldered Inkplate 6Plus in the boards menu."
#endif

#include "Inkplate.h"

#define MAXITERATIONS 150

Inkplate display(INKPLATE_1BIT);

// Takes a long time to render, approx. 3 minutes

// Explore different positions to draw
// Some interesting ones can be found here http://www.cuug.ab.ca/dewara/mandelbrot/Mandelbrowser.html
double xFrom = -0.7423, xTo = -0.8463;
double yFrom = 0.1092, yTo = 0.2102;

void setup()
{
    Serial.begin(115200);
    // Uncomment this line if you have a USB Power Only Inkplate6PLUS
    // Must be called before display.begin()!
    //display.setInkplatePowerMode(INKPLATE_USB_PWR_ONLY);
    display.begin();
}

void loop()
{
    display.clearDisplay();
    for (int j = 0; j < E_INK_HEIGHT; ++j)
    {
        for (int i = 0; i < E_INK_WIDTH; ++i)
            display.drawPixel(
                i, j, colorAt(xFrom + (double)i * (xTo - xFrom) / 800.0, yFrom + (double)j * (yTo - yFrom) / 600.0));
        // for whole set:
        // display.drawPixel(i, j, colorAt(-2.0 + (3.0 * (double)i / 800.0), -1.0 + 2.0 * (double)j / 600.0));
        Serial.println(j);
    }
    display.display();
    delay(5000);
}

struct complex
{
    double re;
    double im;
};

void addComplex(struct complex *z, struct complex *c)
{
    z->re += c->re;
    z->im += c->im;
}

void squareComplex(struct complex *z)
{
    double re = z->re;
    double im = z->im;
    z->re = re * re - im * im;
    z->im = 2 * re * im;
}

double modulusComplexSqr(struct complex *z)
{
    return z->re * z->re + z->im * z->im;
}

uint8_t colorAt(double x, double y)
{
    struct complex z = {0.0, 0.0};
    struct complex c = {x, y};

    int i;
    for (i = 0; i < MAXITERATIONS && modulusComplexSqr(&z) <= 4.0; ++i)
    {
        squareComplex(&z);
        addComplex(&z, &c);
    }
    return i == MAXITERATIONS;
}
