This commit is contained in:
2026-01-13 20:07:21 +00:00
parent abab81ce53
commit 3768da01cc
6 changed files with 236 additions and 76 deletions

191
main.cpp
View File

@@ -131,12 +131,77 @@ float m_c_imag = 0.0f;
std::vector<Slider> mandel_sliders = {
{"Exp", &m_exponent, 1.0f, 10.0f, false},
{"Zr", &m_z_real, -1.0f, 1.0f, false},
{"Zi", &m_z_imag, -1.0f, 1.0f, false},
{"Cr", &m_c_real, -1.0f, 1.0f, false},
{"Ci", &m_c_imag, -1.0f, 1.0f, false}
// {"Zr", &m_z_real, -1.0f, 1.0f, false}, // Removing these to save space/simplify logic for Perturbation
// {"Zi", &m_z_imag, -1.0f, 1.0f, false},
{"Cr", &m_c_real, -0.1f, 0.1f, false}, // Detail C offset
{"Ci", &m_c_imag, -0.1f, 0.1f, false}
};
// --- Deep Zoom (Perturbation) State ---
// Using __float128 (Quad Precision) if available (GCC/Clang usually have it).
// This gives ~34 decimal digits ($10^34$ zoom).
// For true "Infinite", we'd need GMP or a custom BigFloat array class.
// For this demo, __float128 is a massive upgrade over double ($10^15$) and is much faster/safer to implement here.
typedef __float128 DeepFloat;
DeepFloat center_r = -0.5;
DeepFloat center_i = 0.0;
DeepFloat zoom_deep = 1.0;
// Reference Orbit Data (uploaded to texture)
const int MAX_REF_ITER = 1024; // Texture width
std::vector<float> ref_orbit_data(MAX_REF_ITER * 4); // RGBA per step
GLuint ref_orbit_texture = 0;
void calc_reference_orbit() {
DeepFloat zr = 0.0;
DeepFloat zi = 0.0;
DeepFloat cr = center_r;
DeepFloat ci = center_i;
// Upload Z0
ref_orbit_data[0] = (float)zr;
ref_orbit_data[1] = (float)zi;
ref_orbit_data[2] = 0.0f;
ref_orbit_data[3] = 1.0f;
for (int i = 1; i < MAX_REF_ITER; i++) {
DeepFloat zr2 = zr * zr;
DeepFloat zi2 = zi * zi;
DeepFloat two_zr_zi = 2.0 * zr * zi;
zr = zr2 - zi2 + cr;
zi = two_zr_zi + ci;
// Encoding for GPU (Safety against 0..1 clamping)
// Range [-4, 4] -> [0, 1]
float r_encoded = ((float)zr + 4.0f) / 8.0f;
float i_encoded = ((float)zi + 4.0f) / 8.0f;
ref_orbit_data[i * 4 + 0] = r_encoded;
ref_orbit_data[i * 4 + 1] = i_encoded;
ref_orbit_data[i * 4 + 2] = 0.0f;
ref_orbit_data[i * 4 + 3] = 1.0f;
if ((zr2 + zi2) > 4.0) {
// Continue filling
}
}
// Upload to Texture
if (ref_orbit_texture == 0) {
glGenTextures(1, &ref_orbit_texture);
glBindTexture(GL_TEXTURE_2D, ref_orbit_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glBindTexture(GL_TEXTURE_2D, ref_orbit_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MAX_REF_ITER, 1, 0, GL_RGBA, GL_FLOAT, ref_orbit_data.data());
}
// Forward declarations
void render_frame();
GLuint load_shader_src(const char *source, GLenum type);
@@ -339,16 +404,7 @@ float view_x = -0.5f;
float view_y = 0.0f;
bool is_dragging = false;
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
if (cursor_x > SIDEBAR_WIDTH) { // Only zoom in main area
double val = wl_fixed_to_double(value);
if (val < 0) {
zoom_level *= 1.1f;
} else if (val > 0) {
zoom_level /= 1.1f;
}
}
}
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
double old_x = cursor_x;
@@ -358,27 +414,38 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uin
// Slider Logic
if (shaders[current_shader_index].name == "MANDEL") {
for (auto& s : mandel_sliders) {
for (auto& s : mandel_sliders) {
if (s.is_dragging) {
// Calculate new value based on X position relative to sidebar
// Lets say slider width is SIDEBAR_WIDTH - 20
float track_x = 10;
float track_w = SIDEBAR_WIDTH - 20;
float normalized = (float)(cursor_x - track_x) / track_w;
if (normalized < 0.0f) normalized = 0.0f;
if (normalized > 1.0f) normalized = 1.0f;
*s.value_ptr = s.min_val + normalized * (s.max_val - s.min_val);
return; // Consume input
return;
}
}
}
if (is_dragging) {
float dx = (cursor_x - old_x) / height;
float dy = (cursor_y - old_y) / height;
view_x -= dx / zoom_level * 2.0;
view_y += dy / zoom_level * 2.0;
if (shaders[current_shader_index].name == "MANDEL") {
// Deep Pan
double dx = (cursor_x - old_x) / height;
double dy = (cursor_y - old_y) / height;
// Adjust Center (Deep Float)
// Zoom is zoom_deep
DeepFloat scale = 1.0 / zoom_deep;
center_r -= (DeepFloat)dx * scale * 2.0;
center_i += (DeepFloat)dy * scale * 2.0;
} else {
// Normal Pan
float dx = (cursor_x - old_x) / height;
float dy = (cursor_y - old_y) / height;
view_x -= dx / zoom_level * 2.0;
view_y += dy / zoom_level * 2.0;
}
}
}
@@ -386,27 +453,23 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
if (button == 0x110) { // Left click
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
if (cursor_x < SIDEBAR_WIDTH) {
// Check Sliders first if Mandelbrot
bool hit_slider = false;
// Check Sliders
bool hit_slider = false;
if (shaders[current_shader_index].name == "MANDEL") {
// We need to know where they are drawn.
// Let's assume they are drawn starting at y = 500 (bottom of sidebar?)
// Or right after the buttons?
// We have 7 buttons. 7 * 70 = 490px.
float sy = 490 + 20;
for (auto& s : mandel_sliders) {
if (cursor_y >= sy && cursor_y < sy + 20) {
s.is_dragging = true;
hit_slider = true;
}
sy += 30; // 20 height + 10 margin
sy += 30;
}
}
if (!hit_slider) {
int clicked_index = -1;
for (size_t i = 0; i < shaders.size(); ++i) {
int y_start = BUTTON_MARGIN + i * (BUTTON_HEIGHT + BUTTON_MARGIN);
int y_start = BUTTON_MARGIN + i * (BUTTON_HEIGHT + BUTTON_MARGIN);
int y_end = y_start + BUTTON_HEIGHT;
if (cursor_y >= y_start && cursor_y <= y_end && cursor_x >= BUTTON_MARGIN && cursor_x <= SIDEBAR_WIDTH - BUTTON_MARGIN) {
clicked_index = i;
@@ -415,11 +478,13 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
}
if (clicked_index != -1) {
current_shader_index = clicked_index;
// Reset slider drag states just in case
for (auto& s : mandel_sliders) s.is_dragging = false;
if (shaders[current_shader_index].name == "MANDEL") {
// Keep zoom? Yes.
// Reset Deep State
center_r = -0.5;
center_i = 0.0;
zoom_deep = 1.0;
}
}
}
@@ -428,7 +493,27 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uin
}
} else {
is_dragging = false;
for (auto& s : mandel_sliders) s.is_dragging = false; // Release sliders
for (auto& s : mandel_sliders) s.is_dragging = false;
}
}
}
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
if (cursor_x > SIDEBAR_WIDTH) { // Only zoom in main area
double val = wl_fixed_to_double(value);
if (shaders[current_shader_index].name == "MANDEL") {
// Deep Zoom
if (val < 0) {
zoom_deep *= 1.1;
} else if (val > 0) {
zoom_deep /= 1.1;
}
} else {
if (val < 0) {
zoom_level *= 1.1f;
} else if (val > 0) {
zoom_level /= 1.1f;
}
}
}
}
@@ -602,7 +687,41 @@ void render_frame() {
if (zoomLoc != -1) glUniform1f(zoomLoc, zoom_level);
GLint centerLoc = glGetUniformLocation(active.program, "u_center");
if (centerLoc != -1) glUniform2f(centerLoc, view_x, view_y);
if (centerLoc != -1) {
if (active.name == "MANDEL") {
// Pass Deep Orbit Texture
calc_reference_orbit();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, ref_orbit_texture);
GLint refLoc = glGetUniformLocation(active.program, "u_ref_orbit");
if (refLoc != -1) glUniform1i(refLoc, 1); // Unit 1
// Pass Zoom and Center?
// We actually don't need Center if we are doing Delta?
// We need Delta C for the pixel.
// Wait, standard Delta approach:
// dc = (uv) / zoom
// z = 0 + 0 (if starting at 0)
// But u_ref_orbit contains the center's orbit.
// We still need to pass zoom.
glUniform1f(zoomLoc, (float)zoom_deep);
// Center is implicit 0,0 relative to reference?
// No, we still want to visualize.
// Actually, for Delta Shader:
// C_pixel = C_ref + DeltaC
// C_ref is used on CPU.
// DeltaC = (FragCoord - CenterScreen) * PixelScale
// So we assume u_center is 0,0 in the shader logic because we are tracking C_ref on CPU.
glUniform2f(centerLoc, 0.0f, 0.0f);
} else {
glUniform2f(centerLoc, view_x, view_y);
}
}
// Mandelbrot Params
GLint expLoc = glGetUniformLocation(active.program, "u_exponent");