#include "mandelbrot.h"
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_oldnames.h>
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_surface.h>
#include <SDL3/SDL_video.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
extern int points[WINDOW_WIDTH * WINDOW_HEIGHT];
int main(void) {
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) {
fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
return -1;
}
SDL_Window* window =
SDL_CreateWindow(WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN);
if (window == NULL) {
fprintf(stderr, "CreateWindow failed: %s\n", SDL_GetError());
SDL_Quit();
return -1;
}
SDL_Surface* surface = SDL_GetWindowSurface(window);
if (!surface) {
fprintf(stderr, "GetWindowSurface failed: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return -1;
}
SDL_Renderer* renderer = SDL_CreateSoftwareRenderer(surface);
if (renderer == NULL) {
fprintf(stderr, "CreateSoftwareRenderer failed: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return -1;
}
SDL_Event e;
bool running = true;
SDL_ShowWindow(window);
position_t position;
position.x_min = -2.0;
position.x_max = 1.0;
position.y_min = -1.2;
position.y_max = 1.2;
bool pos_updated = true;
char debug_text[256] = "";
bool is_dragging = false;
float drag_start_x, drag_start_y;
double drag_start_x_min, drag_start_x_max;
double drag_start_y_min, drag_start_y_max;
while (running) {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) {
running = false;
}
if (e.type == SDL_EVENT_MOUSE_WHEEL) {
float mx, my;
SDL_GetMouseState(&mx, &my);
double cr =
position.x_min + (position.x_max - position.x_min) * mx / (WINDOW_WIDTH - 1);
double ci =
position.y_min + (position.y_max - position.y_min) * my / (WINDOW_HEIGHT - 1);
double range_y = position.y_max - position.y_min;
float factor = (e.wheel.y > 0) ? 0.9f : 1.1f;
double new_height = range_y * factor;
double aspect = (double)WINDOW_WIDTH / WINDOW_HEIGHT;
double new_width = new_height * aspect;
position.x_min = cr - (double)mx / (WINDOW_WIDTH - 1) * new_width;
position.x_max = position.x_min + new_width;
position.y_min = ci - (double)my / (WINDOW_HEIGHT - 1) * new_height;
position.y_max = position.y_min + new_height;
pos_updated = true;
}
if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (e.button.button == SDL_BUTTON_LEFT) {
is_dragging = true;
drag_start_x = (float)e.button.x;
drag_start_y = (float)e.button.y;
// store current view
drag_start_x_min = position.x_min;
drag_start_x_max = position.x_max;
drag_start_y_min = position.y_min;
drag_start_y_max = position.y_max;
}
float mx, my;
SDL_GetMouseState(&mx, &my);
int ix = (int)floor(mx);
int iy = (int)floor(my);
int iter = points[iy * WINDOW_WIDTH + ix];
snprintf(debug_text, sizeof(debug_text), "x: %d y: %d iterations: %d", ix, iy,
iter);
}
if (e.type == SDL_EVENT_MOUSE_MOTION) {
if (is_dragging) {
float mx = (float)e.motion.x;
float my = (float)e.motion.y;
double dx = mx - drag_start_x;
double dy = my - drag_start_y;
double view_width = drag_start_x_max - drag_start_x_min;
double view_height = drag_start_y_max - drag_start_y_min;
// Pan: shift the view opposite to mouse movement
double x_shift = dx * view_width / (WINDOW_WIDTH - 1);
double y_shift = dy * view_height / (WINDOW_HEIGHT - 1);
position.x_min = drag_start_x_min - x_shift;
position.x_max = drag_start_x_max - x_shift;
position.y_min = drag_start_y_min - y_shift;
position.y_max = drag_start_y_max - y_shift;
pos_updated = true;
}
}
if (e.type == SDL_EVENT_MOUSE_BUTTON_UP) {
if (e.button.button == SDL_BUTTON_LEFT) {
is_dragging = false;
}
}
}
if (pos_updated) {
calculate_points(&position);
pos_updated = false;
}
draw_points(surface);
if (debug_text[0] != '\0') {
SDL_SetRenderDrawColor(renderer, 64, 224, 208, 255);
SDL_RenderDebugText(renderer, 10, 10, debug_text);
}
SDL_RenderPresent(renderer);
SDL_UpdateWindowSurface(window);
SDL_Delay(16);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}