Add preturbation code which doesnt work yet, update to newest build script and run script and preload script

This commit is contained in:
csd4ni3l
2025-12-13 15:14:02 +01:00
parent 9424bf589a
commit 5ef274f60d
9 changed files with 775 additions and 389 deletions

View File

@@ -1,5 +1,7 @@
import arcade, arcade.gui, pyglet, json
from mpmath import mpc
from game.shader import create_iter_calc_shader
from utils.constants import button_style, initial_real_imag
from utils.preload import button_texture, button_hovered_texture, cursor_texture
@@ -14,50 +16,96 @@ class IterFractalViewer(arcade.gui.UIView):
with open("settings.json", "r") as file:
self.settings_dict = json.load(file)
self.escape_radius = int(self.settings_dict.get(f"{self.fractal_name}_escape_radius", 2))
self.real_min, self.real_max, self.imag_min, self.imag_max = initial_real_imag[fractal_name] if fractal_name != "julia" else (-self.escape_radius, self.escape_radius, -self.escape_radius, self.escape_radius)
self.escape_radius = int(
self.settings_dict.get(f"{self.fractal_name}_escape_radius", 2)
)
self.real_min, self.real_max, self.imag_min, self.imag_max = (
initial_real_imag[fractal_name]
if fractal_name != "julia"
else (
-self.escape_radius,
self.escape_radius,
-self.escape_radius,
self.escape_radius,
)
)
self.max_iter = self.settings_dict.get(f"{self.fractal_name}_max_iter", 200)
self.zoom = 1.0
self.zoom_start_position = ()
self.zoom_rect = None
self.has_controller = False
self.dragging_with_controller = False
self.center_real = self.real_min + 0.5 * (self.real_max - self.real_min)
self.center_imag = self.imag_min + 0.5 * (self.imag_max - self.imag_min)
def on_show_view(self):
super().on_show_view()
self.shader_program, self.fractal_image = create_iter_calc_shader(
self.fractal_name,
self.fractal_name,
self.window.width,
self.window.height,
self.settings_dict.get(f"{self.fractal_name}_precision", "Single").lower(),
int(self.settings_dict.get(f"{self.fractal_name}_n", 2)), # This will work for non-exponentiable fractals as well because they dont have an _n property
int(
self.settings_dict.get(f"{self.fractal_name}_n", 2)
), # This will work for non-exponentiable fractals as well because they dont have an _n property
int(self.settings_dict.get(f"{self.fractal_name}_escape_radius", 2)),
self.settings_dict.get("julia_type", "Classic swirling")
self.settings_dict.get("julia_type", "Classic swirling"),
False,
mpc(self.real_min, self.imag_min),
int(self.max_iter),
)
self.fractal_sprite = pyglet.sprite.Sprite(img=self.fractal_image)
self.create_image()
self.pypresence_client.update(state=f'Viewing {self.fractal_name.replace("_", " ").capitalize()}', details=f'Zoom: {self.zoom}\nMax Iterations: {self.max_iter}', start=self.pypresence_client.start_time)
self.pypresence_client.update(
state=f"Viewing {self.fractal_name.replace('_', ' ').capitalize()}",
details=f"Zoom: {self.zoom}\nMax Iterations: {self.max_iter}",
start=self.pypresence_client.start_time,
)
self.setup_ui()
def main_exit(self):
from menus.main import Main
self.window.show_view(Main(self.pypresence_client))
def setup_ui(self):
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
self.info_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10, vertical=False), anchor_x="center", anchor_y="top")
self.zoom_label = self.info_box.add(arcade.gui.UILabel(text=f"Zoom: {self.zoom}", font_name="Roboto", font_size=16))
self.max_iter_label = self.info_box.add(arcade.gui.UILabel(text=f"Max Iterations: {self.max_iter}", font_name="Roboto", font_size=16))
self.info_box = self.anchor.add(
arcade.gui.UIBoxLayout(space_between=10, vertical=False),
anchor_x="center",
anchor_y="top",
)
self.zoom_label = self.info_box.add(
arcade.gui.UILabel(
text=f"Zoom: {self.zoom}", font_name="Roboto", font_size=16
)
)
self.max_iter_label = self.info_box.add(
arcade.gui.UILabel(
text=f"Max Iterations: {self.max_iter}",
font_name="Roboto",
font_size=16,
)
)
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button = arcade.gui.UITextureButton(
texture=button_texture,
texture_hovered=button_hovered_texture,
text="<--",
style=button_style,
width=100,
height=50,
)
self.back_button.on_click = lambda event: self.main_exit()
self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
self.anchor.add(
self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5
)
if self.window.get_controllers():
self.sprite_list = arcade.SpriteList()
@@ -67,7 +115,10 @@ class IterFractalViewer(arcade.gui.UIView):
def on_button_press(self, controller, button_name):
if button_name == "a":
self.zoom_start_position = self.cursor_sprite.center_x, self.cursor_sprite.center_y
self.zoom_start_position = (
self.cursor_sprite.center_x,
self.cursor_sprite.center_y,
)
self.dragging_with_controller = True
elif button_name == "start":
self.main_exit()
@@ -75,26 +126,55 @@ class IterFractalViewer(arcade.gui.UIView):
def on_button_release(self, controller, button_name):
if button_name == "a" and self.dragging_with_controller:
self.dragging_with_controller = False
self.on_mouse_release(self.cursor_sprite.center_x, self.cursor_sprite.center_y, None, None)
self.on_mouse_release(
self.cursor_sprite.center_x, self.cursor_sprite.center_y, None, None
)
def on_stick_motion(self, controller, name, vector):
if name == "leftstick":
self.cursor_sprite.center_x += vector.x * 5
self.cursor_sprite.center_y += vector.y * 5
if self.dragging_with_controller:
self.on_mouse_drag(self.cursor_sprite.center_x, self.cursor_sprite.center_y, 0, 0, None, None)
self.on_mouse_drag(
self.cursor_sprite.center_x,
self.cursor_sprite.center_y,
0,
0,
None,
None,
)
def create_image(self):
with self.shader_program:
self.shader_program['u_maxIter'] = int(self.max_iter)
self.shader_program['u_resolution'] = (self.window.width, self.window.height)
self.shader_program['u_real_range'] = (self.real_min, self.real_max)
self.shader_program['u_imag_range'] = (self.imag_min, self.imag_max)
self.shader_program.dispatch(self.fractal_image.width, self.fractal_image.height, 1, barrier=pyglet.gl.GL_ALL_BARRIER_BITS)
self.shader_program["u_maxIter"] = int(self.max_iter)
self.shader_program["u_resolution"] = (
self.window.width,
self.window.height,
)
self.shader_program["u_real_range"] = (self.real_min, self.real_max)
self.shader_program["u_imag_range"] = (self.imag_min, self.imag_max)
if False:
self.shader_program["u_center"] = (self.center_real, self.center_imag)
self.shader_program["usepreturbation"] = False
self.shader_program.dispatch(
self.fractal_image.width,
self.fractal_image.height,
1,
barrier=pyglet.gl.GL_ALL_BARRIER_BITS,
)
def on_key_press(self, symbol, modifiers):
if symbol == arcade.key.ESCAPE:
self.real_min, self.real_max, self.imag_min, self.imag_max = initial_real_imag[self.fractal_name] if self.fractal_name != "julia" else (-self.escape_radius, self.escape_radius, -self.escape_radius, self.escape_radius)
self.real_min, self.real_max, self.imag_min, self.imag_max = (
initial_real_imag[self.fractal_name]
if self.fractal_name != "julia"
else (
-self.escape_radius,
self.escape_radius,
-self.escape_radius,
self.escape_radius,
)
)
self.zoom = 1
@@ -106,9 +186,9 @@ class IterFractalViewer(arcade.gui.UIView):
self.create_image()
self.pypresence_client.update(
state=f'Viewing {self.fractal_name.replace("_", " ").capitalize()}',
details=f'Zoom: {self.zoom:.2f}\nMax Iterations: {self.max_iter}',
start=self.pypresence_client.start_time
state=f"Viewing {self.fractal_name.replace('_', ' ').capitalize()}",
details=f"Zoom: {self.zoom:.2f}\nMax Iterations: {self.max_iter}",
start=self.pypresence_client.start_time,
)
def on_mouse_drag(self, x, y, dx, dy, _buttons, _modifiers):
@@ -137,7 +217,9 @@ class IterFractalViewer(arcade.gui.UIView):
bottom = min(y0, y1)
top = max(y0, y1)
self.zoom_rect = arcade.rect.LRBT(left=left, right=right, top=top, bottom=bottom)
self.zoom_rect = arcade.rect.LRBT(
left=left, right=right, top=top, bottom=bottom
)
def on_mouse_release(self, x, y, button, modifiers):
if self.zoom_start_position and self.zoom_rect:
@@ -146,18 +228,29 @@ class IterFractalViewer(arcade.gui.UIView):
center_x = (rect.left + rect.right) / 2
center_y = (rect.bottom + rect.top) / 2
center_real = self.real_min + (center_x / self.width) * (self.real_max - self.real_min)
center_imag = self.imag_min + (center_y / self.height) * (self.imag_max - self.imag_min)
self.center_real = self.real_min + (center_x / self.width) * (
self.real_max - self.real_min
)
self.center_imag = self.imag_min + (center_y / self.height) * (
self.imag_max - self.imag_min
)
real_span = (rect.right - rect.left) / self.width * (self.real_max - self.real_min)
imag_span = (rect.top - rect.bottom) / self.height * (self.imag_max - self.imag_min)
real_span = (
(rect.right - rect.left) / self.width * (self.real_max - self.real_min)
)
imag_span = (
(rect.top - rect.bottom) / self.height * (self.imag_max - self.imag_min)
)
self.real_min = center_real - real_span / 2
self.real_max = center_real + real_span / 2
self.imag_min = center_imag - imag_span / 2
self.imag_max = center_imag + imag_span / 2
self.real_min = self.center_real - real_span / 2
self.real_max = self.center_real + real_span / 2
self.imag_min = self.center_imag - imag_span / 2
self.imag_max = self.center_imag + imag_span / 2
initial_real_range = initial_real_imag[self.fractal_name][1] - initial_real_imag[self.fractal_name][0]
initial_real_range = (
initial_real_imag[self.fractal_name][1]
- initial_real_imag[self.fractal_name][0]
)
new_real_range = self.real_max - self.real_min
self.zoom = initial_real_range / new_real_range
@@ -169,9 +262,9 @@ class IterFractalViewer(arcade.gui.UIView):
self.create_image()
self.pypresence_client.update(
state=f'Viewing {self.fractal_name.replace("_", " ").capitalize()}',
details=f'Zoom: {self.zoom:.4f}\nMax Iterations: {self.max_iter}',
start=self.pypresence_client.start_time
state=f"Viewing {self.fractal_name.replace('_', ' ').capitalize()}",
details=f"Zoom: {self.zoom:.4f}\nMax Iterations: {self.max_iter}",
start=self.pypresence_client.start_time,
)
def on_draw(self):
@@ -181,6 +274,6 @@ class IterFractalViewer(arcade.gui.UIView):
if self.has_controller:
self.sprite_list.draw()
if self.zoom_rect:
arcade.draw_rect_outline(self.zoom_rect, color=arcade.color.GRAY)

208
game/preturbation.py Normal file
View File

@@ -0,0 +1,208 @@
from mpmath import mp, mpc
from utils.constants import c_for_julia_type
import array, pyglet
from pyglet.gl import GL_TEXTURE_1D, GL_RG32F, GL_NEAREST
def calculate_julia_orbit(z_start, c_constant, max_iterations):
z = z_start # position
orbit = [z]
for _ in range(max_iterations):
z = z*z + c_constant
orbit.append(z)
return orbit
def calculate_multi_julia_orbit(z_start, c_constant, max_iterations, multi_n):
z = z_start # position
orbit = [z]
for _ in range(max_iterations):
r = abs(z)
theta = mp.atan2(z.imag, z.real)
r_pow = r**multi_n
z = mpc(r_pow * mp.cos(multi_n * theta), r_pow * mp.sin(multi_n * theta)) + c_constant
orbit.append(z)
return orbit
def calculate_mandelbrot_orbit(c_val, max_iterations):
z = mpc(0, 0)
orbit = [z]
for _ in range(max_iterations):
z = z*z + c_val
orbit.append(z)
return orbit
def calculate_multibrot_orbit(c_val, max_iterations, multi_n):
z = mpc(0, 0)
orbit = [z]
for _ in range(max_iterations):
r = abs(z)
theta = mp.atan2(z.imag, z.real)
r_n = r ** multi_n
theta_n = multi_n * theta
z = mpc(r_n * mp.cos(theta_n), r_n * mp.sin(theta_n)) + c_val
orbit.append(z)
return orbit
def calculate_mandelbar_orbit(c_val, max_iterations):
z = mpc(0, 0)
orbit = [z]
for _ in range(max_iterations):
z = z.conjugate() ** 2 + c_val
orbit.append(z)
return orbit
def calculate_multi_mandelbar_orbit(c_val, max_iterations, multi_n):
z = mpc(0, 0)
orbit = [z]
for _ in range(max_iterations):
r = abs(z)
theta = mp.atan2(-z.imag, z.real)
r_n = r ** multi_n
theta_n = multi_n * theta
z = mpc(r_n * mp.cos(theta_n), r_n * mp.sin(theta_n)) + c_val
orbit.append(z)
return orbit
def calculate_buffalo_fractal_orbit(c_val, max_iterations):
z = mpc(0, 0)
orbit = [z]
for _ in range(max_iterations):
z_squared = z * z
z_abs = mpc(abs(z_squared.real), abs(z_squared.imag))
z = z_abs + c_val
orbit.append(z)
return orbit
def calculate_multi_buffalo_fractal_orbit(c_val, max_iterations, multi_n):
z = mpc(0, 0)
orbit = [z]
for _ in range(max_iterations):
r = abs(z)
theta = mp.atan2(z.imag, z.real)
r_n = r ** multi_n
theta_n = multi_n * theta
zn_real = r_n * mp.cos(theta_n)
zn_imag = r_n * mp.sin(theta_n)
z = mpc(abs(zn_real), abs(zn_imag)) + c_val
orbit.append(z)
return orbit
def calculate_burning_ship_orbit(c_val, max_iterations):
z = mpc(0, 0)
orbit = [z]
for _ in range(max_iterations):
x = z.real
y = z.imag
xtemp = x*x - y*y + c_val.real
ytemp = abs(2 * x * y) + c_val.imag
z = mpc(xtemp, ytemp)
orbit.append(z)
return orbit
def calculate_phoenix_fractal_orbit(c_val, max_iterations):
z = mpc(0, 0)
z_prev = mpc(0, 0)
p = 0.56667
orbit = [z]
for _ in range(max_iterations):
z_new = z.conjugate() ** 2 + c_val - p * z_prev
z_prev = z
z = z_new
orbit.append(z)
return orbit
def calculate_lambda_fractal_orbit(c_val, max_iterations):
z = mpc(0.5, 0)
orbit = [z]
number_reverse_complex = mpc(1, 1)
for _ in range(max_iterations):
one_minus_z = number_reverse_complex - z
temp = z * one_minus_z
z = c_val * temp
orbit.append(z)
return orbit
def calculate_orbit(fractal_type, position, max_iterations, multi_n=2, julia_type="Classic swirling"):
if fractal_type == "mandelbrot":
if int(multi_n) == 2:
orbit = calculate_mandelbrot_orbit(position, max_iterations)
else:
orbit = calculate_multibrot_orbit(position, max_iterations, multi_n)
elif fractal_type == "mandelbar":
if int(multi_n) == 2:
orbit = calculate_mandelbar_orbit(position, max_iterations)
else:
orbit = calculate_multi_mandelbar_orbit(position, max_iterations)
elif fractal_type == "phoenix_fractal":
orbit = calculate_phoenix_fractal_orbit(position, max_iterations)
elif fractal_type == "lambda_fractal":
orbit = calculate_lambda_fractal_orbit(position, max_iterations)
elif fractal_type == "julia":
if int(multi_n) == 2:
orbit = calculate_julia_orbit(position, c_for_julia_type[julia_type], max_iterations)
else:
orbit = calculate_multi_julia_orbit(position, c_for_julia_type[julia_type], max_iterations, multi_n)
elif fractal_type == "buffalo_fractal":
if int(multi_n) == 2:
orbit = calculate_buffalo_fractal_orbit(position, max_iterations)
else:
orbit = calculate_multi_buffalo_fractal_orbit(position, max_iterations)
elif fractal_type == "burning_ship":
orbit = calculate_burning_ship_orbit(position, max_iterations)
float_array = array.array('f')
for c_val in orbit:
float_array.append(float(c_val.real))
float_array.append(float(c_val.imag))
orbit_texture = pyglet.image.Texture.create(max_iterations, 1, internalformat=GL_RG32F, target=GL_TEXTURE_1D, min_filter=GL_NEAREST, mag_filter=GL_NEAREST)
orbit_image_data = pyglet.image.ImageData(max_iterations, 1, "RG", float_array.tobytes())
orbit_image_data.blit_to_texture(orbit_texture.target, orbit_texture.level, orbit_texture.anchor_x, orbit_texture.anchor_y, 0, None)
return orbit_texture

View File

@@ -1,5 +1,8 @@
import pyglet
from utils.constants import c_for_julia_type
from game.preturbation import calculate_orbit
from mpmath import mpc
newton_coloring = """vec4 getColor(int color_number) {{
vec4 value = vec4(0.0, 0.0, 0.0, 1.0);
@@ -30,9 +33,9 @@ polynomial_coloring = """vec4 getColor(int iters) {{
"""
fire_coloring = """vec4 getColor(int iters) {{
vec4 value = vec4(0.0, 0.0, 0.0, 1.0);
vec4 value = vec4(1.0, 0.0, 0.0, 1.0);
if (iters != u_maxIter) {{
float t = float(iters) / float(u_maxIter);
float t = float(iters) / float(u_maxIter) + 0.5;
value.r = 3.0 * t;
value.g = 2.0 * t * t;
value.b = t * t * t;
@@ -46,12 +49,55 @@ uniform int u_maxIter;
uniform vec2 u_resolution;
uniform vec2 u_real_range;
uniform vec2 u_imag_range;
uniform vec2 u_center;
uniform sampler2D orbit;
uniform bool usepreturbation;
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(location = 0, rgba32f) uniform image2D img_output;
{coloring_func}
{iter_calc_func}
int calculate_iters({vec2type} pos) {{
int fractal_type = {fractal_type};
{vec2type} z = {initial_z};
{vec2type} c = {initial_c};
if (fractal_type != 0) {{
return int(fractal_iteration(z, c).x);
}}
int iters = 0;
float R = {escape_radius};
if (!usepreturbation) {{
while (dot(z, z) < R * R && iters < u_maxIter) {{
z = fractal_iteration(z, c);
iters++;
}}
return iters;
}}
{vec2type} delta = c - u_center;
{vec2type} eps = {vec2type}(0.0);
{vec2type} zn = {vec2type}(0, 0);
while (dot(zn, zn) < R * R && iters < u_maxIter) {{
vec2 Z = texelFetch(orbit, ivec2(iters, 0), 0).xy;
{vec2type} eps_sq = {vec2type}(eps.x * eps.x - eps.y * eps.y, 2.0 * eps.x * eps.y);
{vec2type} term1 = {vec2type}(2.0 * Z.x * eps.x - 2.0 * Z.y * eps.y, 2.0 * Z.x * eps.y + 2.0 * Z.y * eps.x);
eps = term1 + eps_sq + delta;
zn = Z + eps;
iters++;
}}
return iters;
}}
{vec2type} map_pixel({floattype} x, {floattype} y, {vec2type} resolution, {vec2type} real_range, {vec2type} imag_range) {{
{floattype} real = real_range.x + (x / resolution.x) * (real_range.y - real_range.x);
{floattype} imag = imag_range.x + (y / resolution.y) * (imag_range.y - imag_range.x);
@@ -91,172 +137,98 @@ void main() {{
}}
"""
normal_julia_calc = """int calculate_iters({vec2type} z) {{
int iters = 0;
float R = {escape_radius};
normal_julia_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
int n = {multi_n};
{vec2type} c = {vec2type}{julia_c};
while (dot(z, z) < R * R && iters < u_maxIter){{
{floattype} xtemp = z.x * z.x - z.y * z.y;
z.y = 2 * z.x * z.y + c.y;
z.x = xtemp + c.x;
iters++;
}}
return iters;
{floattype} xtemp = z.x * z.x - z.y * z.y;
return {vec2type}(xtemp + c.x, 2 * z.x * z.y + c.y);
}}
"""
multi_julia_calc = """int calculate_iters(float z) {{
int iters = 0;
float R = {escape_radius};
float n = float({multi_n});
float c = float({julia_c});
multi_julia_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
{floattype} n = {floattype}({multi_n});
{floattype} r = length(z);
{floattype} theta = atan(z.y, z.x);
{floattype} r_pow = pow(r, n);
while (dot(z, z) < R * R && iters < u_maxIter) {{
float r = length(z);
float theta = atan(z.y, z.x);
float r_pow = pow(r, n);
z = vec2(r_pow * cos(n * theta), r_pow * sin(n * theta)) + c;
iters++;
}}
return iters;
return {vec2type}(r_pow * cos(n * theta), r_pow * sin(n * theta)) + c;
}}
"""
mandelbrot_calc = """int calculate_iters({vec2type} c) {{
int iters = 0;
{vec2type} z = {vec2type}(0.0, 0.0);
float R = {escape_radius};
while (dot(z, z) < R * R && iters < u_maxIter) {{
z = {vec2type}(
z.x * z.x - z.y * z.y + c.x,
2.0 * z.x * z.y + c.y
);
iters++;
}}
return iters;
mandelbrot_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
return {vec2type}(
z.x * z.x - z.y * z.y + c.x,
2.0 * z.x * z.y + c.y
);
}}
"""
multibrot_calc = """int calculate_iters(vec2 c) {{
int iters = 0;
vec2 z = vec2(0.0);
float n = {multi_n};
float R = {escape_radius};
multibrot_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
{floattype} n = {multi_n};
{floattype} r = length(z);
{floattype} theta = atan(z.y, z.x);
while (dot(z, z) < R * R && iters < u_maxIter) {{
float r = length(z);
float theta = atan(z.y, z.x);
{floattype} r_n = pow(r, n);
{floattype} theta_n = n * theta;
float r_n = pow(r, n);
float theta_n = n * theta;
return r_n * {vec2type}(cos(theta_n), sin(theta_n)) + c;
}}
"""
z = r_n * vec2(cos(theta_n), sin(theta_n)) + c;
iters++;
}}
return iters;
mandelbar_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
return {vec2type}(
z.x * z.x - z.y * z.y + c.x,
-2.0 * z.x * z.y + c.y
);
}}
"""
mandelbar_calc = """int calculate_iters({vec2type} c) {{
int iters = 0;
{vec2type} z = {vec2type}(0.0, 0.0);
float R = {escape_radius};
multi_mandelbar_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
{floattype} n = {multi_n};
{floattype} r = length(z);
{floattype} theta = atan(-z.y, z.x);
while (dot(z, z) < R * R && iters < u_maxIter) {{
z = {vec2type}(
z.x * z.x - z.y * z.y + c.x,
-2.0 * z.x * z.y + c.y
);
{floattype} r_n = pow(r, n);
{floattype} theta_n = n * theta;
iters++;
}}
return iters;
return r_n * {vec2type}(cos(theta_n), sin(theta_n)) + c;
}}
"""
multi_mandelbar_calc = """int calculate_iters(vec2 c) {{
int iters = 0;
vec2 z = vec2(0.0);
float n = {multi_n};
float R = {escape_radius};
while (dot(z, z) < R * R && iters < u_maxIter) {{
float r = length(z);
float theta = atan(-z.y, z.x);
float r_n = pow(r, n);
float theta_n = n * theta;
z = r_n * vec2(cos(theta_n), sin(theta_n)) + c;
iters++;
}}
return iters;
buffalo_fractal_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
{floattype} z_squared_real = z.x * z.x - z.y * z.y;
{floattype} z_squared_imag = 2.0 * z.x * z.y;
return {vec2type}(abs(z_squared_real) + c.x, abs(z_squared_imag) + c.y);
}}
"""
buffalo_fractal_calc = """int calculate_iters({vec2type} c) {{
int iters = 0;
{vec2type} z = {vec2type}(0.0, 0.0);
{floattype} R = {escape_radius};
while (dot(z, z) < R * R && iters < u_maxIter) {{
{floattype} z_squared_real = z.x * z.x - z.y * z.y;
{floattype} z_squared_imag = 2.0 * z.x * z.y;
z = {vec2type}(abs(z_squared_real) + c.x, abs(z_squared_imag) + c.y);
iters++;
}}
return iters;
multi_buffalo_fractal_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
{floattype} n = {floattype}({multi_n});
{floattype} r = length(z);
{floattype} theta = atan(z.y, z.x);
{floattype} r_n = pow(r, n);
{floattype} theta_n = n * theta;
{floattype} zn_real = r_n * cos(theta_n);
{floattype} zn_imag = r_n * sin(theta_n);
return {vec2type}(abs(zn_real) + c.x, abs(zn_imag) + c.y);
}}
"""
multi_buffalo_fractal_calc = """int calculate_iters(vec2 c) {{
int iters = 0;
vec2 z = vec2(0.0);
float n = {multi_n};
float R = {escape_radius};
while (dot(z, z) < R * R && iters < u_maxIter) {{
float r = length(z);
float theta = atan(z.y, z.x);
float r_n = pow(r, n);
float theta_n = n * theta;
float zn_real = r_n * cos(theta_n);
float zn_imag = r_n * sin(theta_n);
z = vec2(abs(zn_real) + c.x, abs(zn_imag) + c.y);
iters++;
}}
return iters;
burning_ship_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
{floattype} xtemp = z.x * z.x - z.y * z.y + c.x;
return {vec2type}(xtemp, abs(2.0 * z.x * z.y) + c.y);
}}
"""
burning_ship_calc = """int calculate_iters({vec2type} c) {{
int iters = 0;
{vec2type} z = {vec2type}(0.0, 0.0);
float R = {escape_radius};
while (dot(z, z) < R * R && iters < u_maxIter) {{
{floattype} xtemp = z.x * z.x - z.y * z.y + c.x;
z.y = abs(2.0 * z.x * z.y) + c.y;
z.x = xtemp;
iters++;
}}
return iters;
}}
"""
phoenix_fractal_calc = """int calculate_iters({vec2type} c) {{
phoenix_fractal_calc = """{vec2type} fractal_iteration({vec2type} unused, {vec2type} c) {{
int iters = 0;
{vec2type} z = {vec2type}(0.0, 0.0);
{vec2type} z_prev = {vec2type}(0.0, 0.0);
@@ -274,81 +246,76 @@ phoenix_fractal_calc = """int calculate_iters({vec2type} c) {{
iters++;
}}
return iters;
return {vec2type}(iters, 0);
}}
"""
lambda_fractal_calc = """int calculate_iters({vec2type} c) {{
int iters = 0;
{vec2type} z = {vec2type}(0.5, 0.0); // Try nonzero start
float R = {escape_radius}; // Try R = 2.0 if needed
while (dot(z, z) < R * R && iters < u_maxIter) {{
{vec2type} one_minus_z = {vec2type}(1.0, 1.0) - z;
{vec2type} temp = {vec2type}(
z.x * one_minus_z.x - z.y * one_minus_z.y,
z.x * one_minus_z.y + z.y * one_minus_z.x
);
z = {vec2type}(
c.x * temp.x - c.y * temp.y,
c.x * temp.y + c.y * temp.x
);
iters++;
lambda_fractal_calc = """
{vec2type} fractal_iteration ({vec2type} z, {vec2type} c) {{
if (z.x == 0) {{
z.x = 0.5;
}}
return iters;
}}
{vec2type} one_minus_z = {vec2type}(1.0, 1.0) - z;
{vec2type} temp = {vec2type}(
z.x * one_minus_z.x - z.y * one_minus_z.y,
z.x * one_minus_z.y + z.y * one_minus_z.x
);
return {vec2type}(
c.x * temp.x - c.y * temp.y,
c.x * temp.y + c.y * temp.x
);
}}
"""
newton_fractal_calc = """vec2 cmul(vec2 a, vec2 b) {{
return vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
newton_fractal_calc = """{vec2type} cmul({vec2type} a, {vec2type} b) {{
return {vec2type}(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
}}
vec2 cdiv(vec2 a, vec2 b) {{
{vec2type} cdiv({vec2type} a, {vec2type} b) {{
float denom = b.x * b.x + b.y * b.y;
return vec2((a.x * b.x + a.y * b.y) / denom, (a.y * b.x - a.x * b.y) / denom);
return {vec2type}((a.x * b.x + a.y * b.y) / denom, (a.y * b.x - a.x * b.y) / denom);
}}
vec2 cpow(vec2 z, int power) {{
vec2 result = vec2(1.0, 0.0);
{vec2type} cpow({vec2type} z, int power) {{
{vec2type} result = {vec2type}(1.0, 0.0);
for (int i = 0; i < power; ++i) {{
result = cmul(result, z);
}}
return result;
}}
vec2 func(vec2 z) {{
return cpow(z, 3) - vec2(1.0, 0.0);
{vec2type} func({vec2type} z) {{
return cpow(z, 3) - {vec2type}(1.0, 0.0);
}}
vec2 derivative(vec2 z) {{
{vec2type} derivative({vec2type} z) {{
return 3.0 * cmul(z, z);
}}
int calculate_iters(vec2 z) {{
{vec2type} fractal_iteration({vec2type} c, {vec2type} z) {{
float tolerance = 0.000001;
vec2 roots[3] = vec2[](
vec2(1, 0),
vec2(-0.5, 0.866025404),
vec2(-0.5, -0.866025404)
{vec2type} roots[3] = {vec2type}[](
{vec2type}(1, 0),
{vec2type}(-0.5, 0.866025404),
{vec2type}(-0.5, -0.866025404)
);
for (int iters = 0; iters < u_maxIter; iters++) {{
z -= cdiv(func(z), derivative(z));
for (int i = 0; i < 3; i++) {{
vec2 difference = z - roots[i];
{vec2type} difference = z - roots[i];
if (abs(difference.x) < tolerance && abs(difference.y) < tolerance) {{
return i;
return {vec2type}(i, 0);
}}
}}
}}
return -1;
return {vec2type}(-1, 0);
}}
"""
@@ -371,18 +338,25 @@ def create_sierpinsky_carpet_shader(width, height, precision="single"):
return shader_program, sierpinsky_carpet_image
def create_iter_calc_shader(fractal_type, width, height, precision="single", multi_n=2, escape_radius=2, julia_type="Classic swirling"):
def create_iter_calc_shader(fractal_type, width, height, precision="single", multi_n=2, escape_radius=2, julia_type="Classic swirling", use_preturbation=False, preturbation_center=mpc(0, 0), max_iters=0):
shader_source = iter_fractal_template
replacements = {
"multi_n": str(multi_n),
"julia_c": str(c_for_julia_type[julia_type]),
"escape_radius": str(escape_radius),
"vec2type": "dvec2" if int(multi_n) == 2 and precision == "double" else "vec2",
"floattype": "double" if int(multi_n) == 2 and precision == "double" else "float"
}
replacements["coloring_func"] = polynomial_coloring.format_map(replacements)
replacements["coloring_func"] = fire_coloring.format_map(replacements)
replacements["fractal_type"] = 0
if fractal_type == "julia":
replacements["initial_z"] = "pos"
replacements["initial_c"] = f"{replacements['vec2type']}{c_for_julia_type[julia_type]}" # this works because if its vec2, it would be vec2(..., ...) for example.
else:
replacements["initial_z"] = "vec2(0.0)"
replacements["initial_c"] = "pos"
if fractal_type == "mandelbrot":
if int(multi_n) == 2:
@@ -398,6 +372,7 @@ def create_iter_calc_shader(fractal_type, width, height, precision="single", mul
elif fractal_type == "phoenix_fractal":
replacements["iter_calc_func"] = phoenix_fractal_calc.format_map(replacements)
replacements["fractal_type"] = 1
elif fractal_type == "lambda_fractal":
replacements["iter_calc_func"] = lambda_fractal_calc.format_map(replacements)
@@ -422,14 +397,18 @@ def create_iter_calc_shader(fractal_type, width, height, precision="single", mul
elif fractal_type == "newton_fractal":
replacements["coloring_func"] = newton_coloring.format_map(replacements)
replacements["iter_calc_func"] = newton_fractal_calc.format_map(replacements)
replacements["fractal_type"] = 2
shader_source = shader_source.format_map(replacements)
shader_program = pyglet.graphics.shader.ComputeShaderProgram(shader_source)
iter_calc_image = pyglet.image.Texture.create(width, height, internalformat=pyglet.gl.GL_RGBA32F)
fractal_image = pyglet.image.Texture.create(width, height, internalformat=pyglet.gl.GL_RGBA32F)
uniform_location = shader_program['img_output']
iter_calc_image.bind_image_texture(unit=uniform_location)
fractal_image.bind_image_texture(unit=shader_program['img_output'])
return shader_program, iter_calc_image
if use_preturbation:
orbit_image = calculate_orbit(fractal_type, preturbation_center, max_iters, multi_n, julia_type)
orbit_image.bind_image_texture(unit=shader_program['orbit'])
return shader_program, fractal_image