mirror of
https://github.com/csd4ni3l/fractal-viewer.git
synced 2026-01-01 04:13:41 +01:00
change to template-based shaders, move fractal chooser to menus, add
burning ship and newton fractal, add escape radius to more fractals
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
Fractal viewer in Python using compute shaders and the Arcade and Pyglet modules.
|
Fractal viewer in Python using compute shaders and the Arcade and Pyglet modules.
|
||||||
|
|
||||||
Currently supports multiple types of Julia, the Sierpinsky Carpet and Mandelbrot.
|
Currently supports Julia, multi-Julia, Mandelbrot, Multibrot, Burning Ship, Newton Fractal and the Sierpinsky Carpet.
|
||||||
|
|||||||
95
game/burning_ship.py
Normal file
95
game/burning_ship.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import arcade, arcade.gui, pyglet, json
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from game.shader import create_iter_calc_shader
|
||||||
|
from utils.constants import menu_background_color, button_style, burning_ship_initial_real_min, burning_ship_initial_real_max, burning_ship_initial_imag_min, burning_ship_initial_imag_max
|
||||||
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
|
class BurningShipViewer(arcade.gui.UIView):
|
||||||
|
def __init__(self, pypresence_client):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.pypresence_client = pypresence_client
|
||||||
|
self.real_min = burning_ship_initial_real_min
|
||||||
|
self.real_max = burning_ship_initial_real_max
|
||||||
|
self.imag_min = burning_ship_initial_imag_min
|
||||||
|
self.imag_max = burning_ship_initial_imag_max
|
||||||
|
|
||||||
|
with open("settings.json", "r") as file:
|
||||||
|
self.settings_dict = json.load(file)
|
||||||
|
|
||||||
|
self.max_iter = self.settings_dict.get("burning_ship_max_iter", 200)
|
||||||
|
self.zoom = 1.0
|
||||||
|
|
||||||
|
def on_show_view(self):
|
||||||
|
super().on_show_view()
|
||||||
|
|
||||||
|
self.shader_program, self.burning_ship_image = create_iter_calc_shader("burning_ship", self.window.width, self.window.height, self.settings_dict.get("burning_ship_precision", "Single").lower(), 2, int(self.settings_dict.get("burning_ship_escape_radius", 2)))
|
||||||
|
|
||||||
|
self.burning_ship_sprite = pyglet.sprite.Sprite(img=self.burning_ship_image)
|
||||||
|
|
||||||
|
self.create_image()
|
||||||
|
|
||||||
|
self.pypresence_client.update(state='Viewing Burning Ship', 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="Protest Strike", font_size=16))
|
||||||
|
self.max_iter_label = self.info_box.add(arcade.gui.UILabel(text=f"Max Iterations: {self.max_iter}", font_name="Protest Strike", 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.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)
|
||||||
|
|
||||||
|
def zoom_at(self, center_x, center_y, zoom_factor):
|
||||||
|
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)
|
||||||
|
|
||||||
|
new_real_range = (self.real_max - self.real_min) / zoom_factor
|
||||||
|
new_imag_range = (self.imag_max - self.imag_min) / zoom_factor
|
||||||
|
|
||||||
|
self.real_min = center_real - new_real_range / 2
|
||||||
|
self.real_max = center_real + new_real_range / 2
|
||||||
|
self.imag_min = center_imag - new_imag_range / 2
|
||||||
|
self.imag_max = center_imag + new_imag_range / 2
|
||||||
|
|
||||||
|
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.burning_ship_image.width, self.burning_ship_image.height, 1, barrier=pyglet.gl.GL_ALL_BARRIER_BITS)
|
||||||
|
|
||||||
|
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int) -> bool | None:
|
||||||
|
super().on_mouse_press(x, y, button, modifiers)
|
||||||
|
|
||||||
|
if button == arcade.MOUSE_BUTTON_LEFT:
|
||||||
|
zoom = self.settings_dict.get("burning_ship_zoom_increase", 2)
|
||||||
|
elif button == arcade.MOUSE_BUTTON_RIGHT:
|
||||||
|
zoom = 1 / self.settings_dict.get("burning_ship_zoom_increase", 2)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.zoom *= zoom
|
||||||
|
|
||||||
|
self.zoom_label.text = f"Zoom: {self.zoom}"
|
||||||
|
|
||||||
|
self.zoom_at(self.window.mouse.data["x"], self.window.mouse.data["y"], zoom)
|
||||||
|
self.create_image()
|
||||||
|
|
||||||
|
self.pypresence_client.update(state='Viewing Burning Ship', details=f'Zoom: {self.zoom}\nMax Iterations: {self.max_iter}', start=self.pypresence_client.start_time)
|
||||||
|
|
||||||
|
def on_draw(self):
|
||||||
|
self.window.clear()
|
||||||
|
self.burning_ship_sprite.draw()
|
||||||
|
self.ui.draw()
|
||||||
@@ -2,7 +2,7 @@ import arcade, arcade.gui, pyglet, json
|
|||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from game.shader import create_julia_shader
|
from game.shader import create_iter_calc_shader
|
||||||
from utils.constants import menu_background_color, button_style
|
from utils.constants import menu_background_color, button_style
|
||||||
from utils.preload import button_texture, button_hovered_texture
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class JuliaViewer(arcade.gui.UIView):
|
|||||||
def on_show_view(self):
|
def on_show_view(self):
|
||||||
super().on_show_view()
|
super().on_show_view()
|
||||||
|
|
||||||
self.shader_program, self.julia_image = create_julia_shader(self.window.width, self.window.height, self.settings_dict.get("julia_precision", "Single").lower(), self.settings_dict.get("julia_escape_radius", 2), self.settings_dict.get("julia_type", "Classic swirling"), int(self.settings_dict.get("julia_n", 2)))
|
self.shader_program, self.julia_image = create_iter_calc_shader("julia", self.window.width, self.window.height, self.settings_dict.get("julia_precision", "Single").lower(), int(self.settings_dict.get("julia_n", 2)), self.settings_dict.get("julia_escape_radius", 2), self.settings_dict.get("julia_type", "Classic swirling"))
|
||||||
|
|
||||||
self.julia_sprite = pyglet.sprite.Sprite(img=self.julia_image)
|
self.julia_sprite = pyglet.sprite.Sprite(img=self.julia_image)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import arcade, arcade.gui, pyglet, json
|
|||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from game.shader import create_mandelbrot_shader
|
from game.shader import create_iter_calc_shader
|
||||||
from utils.constants import menu_background_color, button_style, mandelbrot_initial_real_min, mandelbrot_initial_real_max, mandelbrot_initial_imag_min, mandelbrot_initial_imag_max
|
from utils.constants import menu_background_color, button_style, mandelbrot_initial_real_min, mandelbrot_initial_real_max, mandelbrot_initial_imag_min, mandelbrot_initial_imag_max
|
||||||
from utils.preload import button_texture, button_hovered_texture
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class MandelbrotViewer(arcade.gui.UIView):
|
|||||||
def on_show_view(self):
|
def on_show_view(self):
|
||||||
super().on_show_view()
|
super().on_show_view()
|
||||||
|
|
||||||
self.shader_program, self.mandelbrot_image = create_mandelbrot_shader(self.window.width, self.window.height, self.settings_dict.get("mandelbrot_precision", "Single").lower())
|
self.shader_program, self.mandelbrot_image = create_iter_calc_shader("mandelbrot", self.window.width, self.window.height, self.settings_dict.get("mandelbrot_precision", "Single").lower(), int(self.settings_dict.get("mandelbrot_n", 2)), int(self.settings_dict.get("mandelbrot_escape_radius", 2)))
|
||||||
|
|
||||||
self.mandelbrot_sprite = pyglet.sprite.Sprite(img=self.mandelbrot_image)
|
self.mandelbrot_sprite = pyglet.sprite.Sprite(img=self.mandelbrot_image)
|
||||||
|
|
||||||
|
|||||||
95
game/newton_fractal.py
Normal file
95
game/newton_fractal.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import arcade, arcade.gui, pyglet, json
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from game.shader import create_iter_calc_shader
|
||||||
|
from utils.constants import menu_background_color, button_style, newton_fractal_initial_real_min, newton_fractal_initial_real_max, newton_fractal_initial_imag_min, newton_fractal_initial_imag_max
|
||||||
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
|
class NewtonFractalViewer(arcade.gui.UIView):
|
||||||
|
def __init__(self, pypresence_client):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.pypresence_client = pypresence_client
|
||||||
|
self.real_min = newton_fractal_initial_real_min
|
||||||
|
self.real_max = newton_fractal_initial_real_max
|
||||||
|
self.imag_min = newton_fractal_initial_imag_min
|
||||||
|
self.imag_max = newton_fractal_initial_imag_max
|
||||||
|
|
||||||
|
with open("settings.json", "r") as file:
|
||||||
|
self.settings_dict = json.load(file)
|
||||||
|
|
||||||
|
self.max_iter = self.settings_dict.get("newton_fractal_max_iter", 200)
|
||||||
|
self.zoom = 1.0
|
||||||
|
|
||||||
|
def on_show_view(self):
|
||||||
|
super().on_show_view()
|
||||||
|
|
||||||
|
self.shader_program, self.newton_fractal_image = create_iter_calc_shader("newton_fractal", self.window.width, self.window.height, self.settings_dict.get("newton_fractal_precision", "Single").lower(), 2, int(self.settings_dict.get("newton_fractal_escape_radius", 2)))
|
||||||
|
|
||||||
|
self.newton_fractal_sprite = pyglet.sprite.Sprite(img=self.newton_fractal_image)
|
||||||
|
|
||||||
|
self.create_image()
|
||||||
|
|
||||||
|
self.pypresence_client.update(state='Viewing Newton Fractal', 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="Protest Strike", font_size=16))
|
||||||
|
self.max_iter_label = self.info_box.add(arcade.gui.UILabel(text=f"Max Iterations: {self.max_iter}", font_name="Protest Strike", 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.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)
|
||||||
|
|
||||||
|
def zoom_at(self, center_x, center_y, zoom_factor):
|
||||||
|
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)
|
||||||
|
|
||||||
|
new_real_range = (self.real_max - self.real_min) / zoom_factor
|
||||||
|
new_imag_range = (self.imag_max - self.imag_min) / zoom_factor
|
||||||
|
|
||||||
|
self.real_min = center_real - new_real_range / 2
|
||||||
|
self.real_max = center_real + new_real_range / 2
|
||||||
|
self.imag_min = center_imag - new_imag_range / 2
|
||||||
|
self.imag_max = center_imag + new_imag_range / 2
|
||||||
|
|
||||||
|
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.newton_fractal_image.width, self.newton_fractal_image.height, 1, barrier=pyglet.gl.GL_ALL_BARRIER_BITS)
|
||||||
|
|
||||||
|
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int) -> bool | None:
|
||||||
|
super().on_mouse_press(x, y, button, modifiers)
|
||||||
|
|
||||||
|
if button == arcade.MOUSE_BUTTON_LEFT:
|
||||||
|
zoom = self.settings_dict.get("newton_fractal_zoom_increase", 2)
|
||||||
|
elif button == arcade.MOUSE_BUTTON_RIGHT:
|
||||||
|
zoom = 1 / self.settings_dict.get("newton_fractal_zoom_increase", 2)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.zoom *= zoom
|
||||||
|
|
||||||
|
self.zoom_label.text = f"Zoom: {self.zoom}"
|
||||||
|
|
||||||
|
self.zoom_at(self.window.mouse.data["x"], self.window.mouse.data["y"], zoom)
|
||||||
|
self.create_image()
|
||||||
|
|
||||||
|
self.pypresence_client.update(state='Viewing Newton Fractal', details=f'Zoom: {self.zoom}\nMax Iterations: {self.max_iter}', start=self.pypresence_client.start_time)
|
||||||
|
|
||||||
|
def on_draw(self):
|
||||||
|
self.window.clear()
|
||||||
|
self.newton_fractal_sprite.draw()
|
||||||
|
self.ui.draw()
|
||||||
356
game/shader.py
356
game/shader.py
@@ -1,156 +1,245 @@
|
|||||||
import pyglet
|
import pyglet
|
||||||
from utils.constants import c_for_julia_type
|
from utils.constants import c_for_julia_type
|
||||||
|
|
||||||
mandelbrot_compute_source = """#version 430 core
|
newton_coloring = """vec4 getColor(int color_number) {{
|
||||||
|
vec4 value = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
if (color_number == 0) {{
|
||||||
|
value.r = 1.0;
|
||||||
|
}}
|
||||||
|
else if (color_number == 1) {{
|
||||||
|
value.g = 1.0;
|
||||||
|
}}
|
||||||
|
else if (color_number == 2) {{
|
||||||
|
value.b = 1.0;
|
||||||
|
}}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
polynomial_coloring = """vec4 getColor(int iters) {{
|
||||||
|
vec4 value = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
if (iters != u_maxIter) {{
|
||||||
|
float t = float(iters) / float(u_maxIter);
|
||||||
|
value.r = 9.0 * (1.0 - t) * t * t * t;
|
||||||
|
value.g = 15.0 * (1.0 - t) * (1.0 - t) * t * t;
|
||||||
|
value.b = 8.5 * (1.0 - t) * (1.0 - t) * (1.0 - t) * t;
|
||||||
|
}}
|
||||||
|
return value;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
fire_coloring = """vec4 getColor(int iters) {{
|
||||||
|
vec4 value = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
if (iters != u_maxIter) {{
|
||||||
|
float t = float(iters) / float(u_maxIter);
|
||||||
|
value.r = 3.0 * t;
|
||||||
|
value.g = 2.0 * t * t;
|
||||||
|
value.b = t * t * t;
|
||||||
|
}}
|
||||||
|
return value;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
iter_fractal_template = """#version 430 core
|
||||||
uniform int u_maxIter;
|
uniform int u_maxIter;
|
||||||
uniform vec2 u_resolution;
|
uniform vec2 u_resolution;
|
||||||
uniform vec2 u_real_range;
|
uniform vec2 u_real_range;
|
||||||
uniform vec2 u_imag_range;
|
uniform vec2 u_imag_range;
|
||||||
|
|
||||||
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
layout(location = 0, rgba32f) uniform image2D img_output;
|
layout(location = 0, rgba32f) uniform image2D img_output;
|
||||||
|
|
||||||
int mandelbrot({vec2type} c, int maxIter) {
|
{coloring_func}
|
||||||
{vec2type} z = {vec2type}(0.0, 0.0);
|
{iter_calc_func}
|
||||||
for (int n = 0; n < maxIter; n++) {
|
|
||||||
if (dot(z, z) > 4.0) {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
z = {vec2type}(
|
|
||||||
z.x * z.x - z.y * z.y + c.x,
|
|
||||||
2.0 * z.x * z.y + c.y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return maxIter;
|
|
||||||
}
|
|
||||||
|
|
||||||
{vec2type} map_pixel({floattype} x, {floattype} y, {vec2type} resolution, {vec2type} real_range, {vec2type} imag_range) {
|
{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} 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);
|
{floattype} imag = imag_range.x + (y / resolution.y) * (imag_range.y - imag_range.x);
|
||||||
return {vec2type}(real, imag);
|
return {vec2type}(real, imag);
|
||||||
}
|
}}
|
||||||
|
|
||||||
void main() {
|
void main() {{
|
||||||
ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);
|
ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);
|
||||||
{vec2type} c = map_pixel({floattype}(texel_coord.x), {floattype}(texel_coord.y), u_resolution, u_real_range, u_imag_range);
|
{vec2type} pos = map_pixel({floattype}(texel_coord.x), {floattype}(texel_coord.y), u_resolution, u_real_range, u_imag_range);
|
||||||
int iters = mandelbrot(c, u_maxIter);
|
int iters = calculate_iters(pos);
|
||||||
|
vec4 value = getColor(iters);
|
||||||
vec4 value = vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
if (iters != u_maxIter) {
|
|
||||||
float t = float(iters) / float(u_maxIter);
|
|
||||||
|
|
||||||
value.r = 9.0 * (1.0 - t) * t * t * t;
|
|
||||||
value.g = 15.0 * (1.0 - t) * (1.0 - t) * t * t;
|
|
||||||
value.b = 8.5 * (1.0 - t) * (1.0 - t) * (1.0 - t) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
imageStore(img_output, texel_coord, value);
|
imageStore(img_output, texel_coord, value);
|
||||||
}
|
}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sierpinsky_carpet_compute_source = """#version 430 core
|
sierpinsky_carpet_compute_source = """#version 430 core
|
||||||
|
|
||||||
uniform int u_depth;
|
uniform int u_depth;
|
||||||
uniform int u_zoom;
|
uniform int u_zoom;
|
||||||
uniform vec2 u_center;
|
uniform vec2 u_center;
|
||||||
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
layout(location = 0, rgba32f) uniform image2D img_output;
|
layout(location = 0, rgba32f) uniform image2D img_output;
|
||||||
|
void main() {{
|
||||||
void main() {
|
|
||||||
{vec2type} centered = {vec2type}(gl_GlobalInvocationID.xy) - u_center;
|
{vec2type} centered = {vec2type}(gl_GlobalInvocationID.xy) - u_center;
|
||||||
{vec2type} zoomed = centered / u_zoom;
|
{vec2type} zoomed = centered / u_zoom;
|
||||||
{vec2type} final_coord = zoomed + u_center;
|
{vec2type} final_coord = zoomed + u_center;
|
||||||
|
|
||||||
ivec2 coord = ivec2(final_coord);
|
ivec2 coord = ivec2(final_coord);
|
||||||
|
|
||||||
bool isHole = false;
|
bool isHole = false;
|
||||||
|
for (int i = 0; i < u_depth; ++i) {{
|
||||||
for (int i = 0; i < u_depth; ++i) {
|
if (coord.x % 3 == 1 && coord.y % 3 == 1) {{
|
||||||
if (coord.x % 3 == 1 && coord.y % 3 == 1) {
|
|
||||||
isHole = true;
|
isHole = true;
|
||||||
break;
|
break;
|
||||||
}
|
}}
|
||||||
coord /= 3;
|
coord /= 3;
|
||||||
}
|
}}
|
||||||
|
|
||||||
vec4 color = isHole ? vec4(0, 0, 0, 1) : vec4(1, 1, 1, 1);
|
vec4 color = isHole ? vec4(0, 0, 0, 1) : vec4(1, 1, 1, 1);
|
||||||
imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), color);
|
imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), color);
|
||||||
}
|
}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
julia_template = """#version 430 core
|
|
||||||
|
|
||||||
uniform int u_maxIter;
|
|
||||||
uniform vec2 u_resolution;
|
|
||||||
uniform vec2 u_real_range;
|
|
||||||
uniform vec2 u_imag_range;
|
|
||||||
|
|
||||||
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
|
||||||
layout(location = 0, rgba32f) uniform image2D img_output;
|
|
||||||
|
|
||||||
{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);
|
|
||||||
return {vec2type}(real, imag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
ivec2 texel_coord = ivec2(gl_GlobalInvocationID.xy);
|
|
||||||
|
|
||||||
float R = {escape_radius};
|
|
||||||
int n = {julia_n};
|
|
||||||
{vec2type} c = {vec2type}{julia_c};
|
|
||||||
|
|
||||||
{vec2type} z = map_pixel({floattype}(texel_coord.x), {floattype}(texel_coord.y), u_resolution, u_real_range, u_imag_range);
|
|
||||||
|
|
||||||
|
normal_julia_calc = """int calculate_iters({vec2type} z) {{
|
||||||
int iters = 0;
|
int iters = 0;
|
||||||
|
float R = {escape_radius};
|
||||||
{julia_calc}
|
int n = {multi_n};
|
||||||
|
{vec2type} c = {vec2type}{julia_c};
|
||||||
vec4 value = vec4(0.0, 0.0, 0.0, 1.0);
|
while (dot(z, z) < R * R && iters < u_maxIter){{
|
||||||
|
|
||||||
if (iters != u_maxIter) {
|
|
||||||
float t = float(iters) / float(u_maxIter);
|
|
||||||
|
|
||||||
value.r = 9.0 * (1.0 - t) * t * t * t;
|
|
||||||
value.g = 15.0 * (1.0 - t) * (1.0 - t) * t * t;
|
|
||||||
value.b = 8.5 * (1.0 - t) * (1.0 - t) * (1.0 - t) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
imageStore(img_output, texel_coord, value);
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
normal_julia_calc = """while (z.x * z.x + z.y * z.y < R*R && iters < u_maxIter)
|
|
||||||
{
|
|
||||||
{floattype} xtemp = z.x * z.x - z.y * z.y;
|
{floattype} xtemp = z.x * z.x - z.y * z.y;
|
||||||
z.y = 2 * z.x * z.y + c.y;
|
z.y = 2 * z.x * z.y + c.y;
|
||||||
z.x = xtemp + c.x;
|
z.x = xtemp + c.x;
|
||||||
|
iters++;
|
||||||
iters = iters + 1;
|
}}
|
||||||
}
|
return iters;
|
||||||
|
}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
multi_julia_calc = """while ((z.x * z.x + z.y * z.y) < R*R && iters < u_maxIter) {
|
multi_julia_calc = """int calculate_iters(float z) {{
|
||||||
{floattype} xtmp = pow((z.x * z.x + z.y * z.y), (n / 2)) * cos(n * atan(z.y, z.x)) + c.x;
|
int iters = 0;
|
||||||
z.y = pow((z.x * z.x + z.y * z.y), (n / 2)) * sin(n * atan(z.y, z.x)) + c.y;
|
float R = {escape_radius};
|
||||||
z.x = xtmp;
|
float n = float({multi_n});
|
||||||
|
float c = float({julia_c});
|
||||||
|
|
||||||
iters = iters + 1;
|
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;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
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;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
multibrot_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;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
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;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
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);
|
||||||
|
}}
|
||||||
|
|
||||||
|
vec2 cdiv(vec2 a, vec2 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);
|
||||||
|
}}
|
||||||
|
|
||||||
|
vec2 cpow(vec2 z, int power) {{
|
||||||
|
vec2 result = vec2(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);
|
||||||
|
}}
|
||||||
|
|
||||||
|
vec2 derivative(vec2 z) {{
|
||||||
|
return 3.0 * cmul(z, z);
|
||||||
|
}}
|
||||||
|
|
||||||
|
int calculate_iters(vec2 z) {{
|
||||||
|
float tolerance = 0.000001;
|
||||||
|
vec2 roots[3] = vec2[](
|
||||||
|
vec2(1, 0),
|
||||||
|
vec2(-0.5, 0.866025404),
|
||||||
|
vec2(-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];
|
||||||
|
if (abs(difference.x) < tolerance && abs(difference.y) < tolerance) {{
|
||||||
|
return i;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def create_sierpinsky_carpet_shader(width, height, precision="single"):
|
def create_sierpinsky_carpet_shader(width, height, precision="single"):
|
||||||
shader_source = sierpinsky_carpet_compute_source
|
shader_source = sierpinsky_carpet_compute_source
|
||||||
|
|
||||||
if precision == "single":
|
replacements = {
|
||||||
shader_source = shader_source.replace("{vec2type}", "vec2").replace("{floattype}", "float")
|
"vec2type": "dvec2" if precision == "double" else "vec2",
|
||||||
elif precision == "double":
|
"floattype": "double" if precision == "double" else "float"
|
||||||
shader_source = shader_source.replace("{vec2type}", "dvec2").replace("{floattype}", "double")
|
}
|
||||||
else:
|
|
||||||
raise TypeError("Invalid Precision")
|
shader_source = shader_source.format_map(replacements)
|
||||||
|
|
||||||
shader_program = pyglet.graphics.shader.ComputeShaderProgram(shader_source)
|
shader_program = pyglet.graphics.shader.ComputeShaderProgram(shader_source)
|
||||||
|
|
||||||
@@ -161,27 +250,40 @@ def create_sierpinsky_carpet_shader(width, height, precision="single"):
|
|||||||
|
|
||||||
return shader_program, sierpinsky_carpet_image
|
return shader_program, sierpinsky_carpet_image
|
||||||
|
|
||||||
def create_julia_shader(width, height, precision="single", escape_radius=2, julia_type="Classic swirling", julia_n=2):
|
def create_iter_calc_shader(fractal_type, width, height, precision="single", multi_n=2, escape_radius=2, julia_type="Classic swirling"):
|
||||||
shader_source = julia_template
|
shader_source = iter_fractal_template
|
||||||
|
|
||||||
if julia_n == 2:
|
replacements = {
|
||||||
shader_source = shader_source.replace("{julia_calc}", normal_julia_calc)
|
"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"
|
||||||
|
}
|
||||||
|
|
||||||
if precision == "single":
|
replacements["coloring_func"] = polynomial_coloring.format_map(replacements)
|
||||||
shader_source = shader_source.replace("{vec2type}", "vec2").replace("{floattype}", "float")
|
|
||||||
elif precision == "double":
|
|
||||||
shader_source = shader_source.replace("{vec2type}", "dvec2").replace("{floattype}", "double")
|
|
||||||
|
|
||||||
|
if fractal_type == "mandelbrot":
|
||||||
|
if int(multi_n) == 2:
|
||||||
|
replacements["iter_calc_func"] = mandelbrot_calc.format_map(replacements)
|
||||||
else:
|
else:
|
||||||
shader_source = shader_source.replace("{julia_calc}", multi_julia_calc)
|
replacements["iter_calc_func"] = multibrot_calc.format_map(replacements)
|
||||||
shader_source = shader_source.replace("{vec2type}", "vec2").replace("{floattype}", "float") # pow and atan only support floats
|
|
||||||
|
|
||||||
shader_source = shader_source.replace("{julia_n}", str(julia_n))
|
elif fractal_type == "julia":
|
||||||
|
if int(multi_n) == 2:
|
||||||
|
replacements["iter_calc_func"] = normal_julia_calc.format_map(replacements)
|
||||||
|
else:
|
||||||
|
replacements["iter_calc_func"] = multi_julia_calc.format_map(replacements)
|
||||||
|
|
||||||
julia_c = c_for_julia_type[julia_type]
|
elif fractal_type == "burning_ship":
|
||||||
shader_source = shader_source.replace("{julia_c}", str(julia_c))
|
replacements["coloring_func"] = fire_coloring.format_map(replacements)
|
||||||
|
replacements["iter_calc_func"] = burning_ship_calc.format_map(replacements)
|
||||||
|
|
||||||
shader_source = shader_source.replace("{escape_radius}", str(escape_radius))
|
elif fractal_type == "newton_fractal":
|
||||||
|
replacements["coloring_func"] = newton_coloring.format_map(replacements)
|
||||||
|
replacements["iter_calc_func"] = newton_fractal_calc.format_map(replacements)
|
||||||
|
|
||||||
|
shader_source = shader_source.format_map(replacements)
|
||||||
|
|
||||||
shader_program = pyglet.graphics.shader.ComputeShaderProgram(shader_source)
|
shader_program = pyglet.graphics.shader.ComputeShaderProgram(shader_source)
|
||||||
|
|
||||||
@@ -191,23 +293,3 @@ def create_julia_shader(width, height, precision="single", escape_radius=2, juli
|
|||||||
julia_image.bind_image_texture(unit=uniform_location)
|
julia_image.bind_image_texture(unit=uniform_location)
|
||||||
|
|
||||||
return shader_program, julia_image
|
return shader_program, julia_image
|
||||||
|
|
||||||
|
|
||||||
def create_mandelbrot_shader(width, height, precision="single"):
|
|
||||||
shader_source = mandelbrot_compute_source
|
|
||||||
|
|
||||||
if precision == "single":
|
|
||||||
shader_source = shader_source.replace("{vec2type}", "vec2").replace("{floattype}", "float")
|
|
||||||
elif precision == "double":
|
|
||||||
shader_source = shader_source.replace("{vec2type}", "dvec2").replace("{floattype}", "double")
|
|
||||||
else:
|
|
||||||
raise TypeError("Invalid Precision")
|
|
||||||
|
|
||||||
shader_program = pyglet.graphics.shader.ComputeShaderProgram(shader_source)
|
|
||||||
|
|
||||||
mandelbrot_image = pyglet.image.Texture.create(width, height, internalformat=pyglet.gl.GL_RGBA32F)
|
|
||||||
|
|
||||||
uniform_location = shader_program['img_output']
|
|
||||||
mandelbrot_image.bind_image_texture(unit=uniform_location)
|
|
||||||
|
|
||||||
return shader_program, mandelbrot_image
|
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ class FractalChooser(arcade.gui.UIView):
|
|||||||
self.julia_button = self.grid.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Julia', style=button_style, width=200, height=200), row=0, column=2)
|
self.julia_button = self.grid.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Julia', style=button_style, width=200, height=200), row=0, column=2)
|
||||||
self.julia_button.on_click = lambda event: self.julia()
|
self.julia_button.on_click = lambda event: self.julia()
|
||||||
|
|
||||||
|
self.burning_ship_button = self.grid.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Burning Ship', style=button_style, width=200, height=200), row=1, column=0)
|
||||||
|
self.burning_ship_button.on_click = lambda event: self.burning_ship()
|
||||||
|
|
||||||
|
self.newton_fractal_button = self.grid.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Newton Fractal', style=button_style, width=200, height=200), row=1, column=1)
|
||||||
|
self.newton_fractal_button.on_click = lambda event: self.newton_fractal()
|
||||||
|
|
||||||
def main_exit(self):
|
def main_exit(self):
|
||||||
from menus.main import Main
|
from menus.main import Main
|
||||||
self.window.show_view(Main(self.pypresence_client))
|
self.window.show_view(Main(self.pypresence_client))
|
||||||
@@ -47,3 +53,11 @@ class FractalChooser(arcade.gui.UIView):
|
|||||||
def julia(self):
|
def julia(self):
|
||||||
from game.julia import JuliaViewer
|
from game.julia import JuliaViewer
|
||||||
self.window.show_view(JuliaViewer(self.pypresence_client))
|
self.window.show_view(JuliaViewer(self.pypresence_client))
|
||||||
|
|
||||||
|
def burning_ship(self):
|
||||||
|
from game.burning_ship import BurningShipViewer
|
||||||
|
self.window.show_view(BurningShipViewer(self.pypresence_client))
|
||||||
|
|
||||||
|
def newton_fractal(self):
|
||||||
|
from game.newton_fractal import NewtonFractalViewer
|
||||||
|
self.window.show_view(NewtonFractalViewer(self.pypresence_client))
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import arcade, arcade.gui, asyncio, pypresence, time, copy, json
|
import arcade, arcade.gui, asyncio, pypresence, time, copy, json
|
||||||
from game.play import FractalChooser
|
|
||||||
from utils.preload import button_texture, button_hovered_texture
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
from utils.constants import big_button_style, discord_presence_id
|
from utils.constants import big_button_style, discord_presence_id
|
||||||
from utils.utils import FakePyPresence
|
from utils.utils import FakePyPresence
|
||||||
@@ -60,7 +59,7 @@ class Main(arcade.gui.UIView):
|
|||||||
self.settings_button.on_click = lambda event: self.settings()
|
self.settings_button.on_click = lambda event: self.settings()
|
||||||
|
|
||||||
def play(self):
|
def play(self):
|
||||||
from game.play import FractalChooser
|
from menus.fractal_chooser import FractalChooser
|
||||||
self.window.show_view(FractalChooser(self.pypresence_client))
|
self.window.show_view(FractalChooser(self.pypresence_client))
|
||||||
|
|
||||||
def settings(self):
|
def settings(self):
|
||||||
|
|||||||
@@ -12,6 +12,16 @@ mandelbrot_initial_real_max = 1.0
|
|||||||
mandelbrot_initial_imag_min = -1.0
|
mandelbrot_initial_imag_min = -1.0
|
||||||
mandelbrot_initial_imag_max = 1.0
|
mandelbrot_initial_imag_max = 1.0
|
||||||
|
|
||||||
|
burning_ship_initial_real_min = -2.0
|
||||||
|
burning_ship_initial_real_max = 1.5
|
||||||
|
burning_ship_initial_imag_min = -2.0
|
||||||
|
burning_ship_initial_imag_max = 1.0
|
||||||
|
|
||||||
|
newton_fractal_initial_real_min = -2.0
|
||||||
|
newton_fractal_initial_real_max = 2.0
|
||||||
|
newton_fractal_initial_imag_min = -2.0
|
||||||
|
newton_fractal_initial_imag_max = 2.0
|
||||||
|
|
||||||
c_for_julia_type = {
|
c_for_julia_type = {
|
||||||
"Classic swirling": (-0.7, 0.27015),
|
"Classic swirling": (-0.7, 0.27015),
|
||||||
"Douady rabbit": (-0.123, 0.745),
|
"Douady rabbit": (-0.123, 0.745),
|
||||||
@@ -35,9 +45,17 @@ slider_style = {'normal': slider_default_style, 'hover': slider_hover_style, 'pr
|
|||||||
settings = {
|
settings = {
|
||||||
"Mandelbrot": {
|
"Mandelbrot": {
|
||||||
"Float Precision": {"type": "option", "options": ["Single", "Double"], "config_key": "mandelbrot_precision", "default": "Single"},
|
"Float Precision": {"type": "option", "options": ["Single", "Double"], "config_key": "mandelbrot_precision", "default": "Single"},
|
||||||
|
"N": {"type": "slider", "min": 1, "max": 10, "config_key": "mandelbrot_n", "default": 2, "step": 1},
|
||||||
|
"Escape Radius": {"type": "slider", "min": 1, "max": 10, "config_key": "mandelbrot_escape_radius", "default": 2, "step": 0.1},
|
||||||
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "mandelbrot_zoom_increase", "default": 2},
|
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "mandelbrot_zoom_increase", "default": 2},
|
||||||
"Max Iterations": {"type": "slider", "min": 100, "max": 10000, "config_key": "mandelbrot_max_iter", "default": 200, "step": 100}
|
"Max Iterations": {"type": "slider", "min": 100, "max": 10000, "config_key": "mandelbrot_max_iter", "default": 200, "step": 100}
|
||||||
},
|
},
|
||||||
|
"Burning Ship": {
|
||||||
|
"Float Precision": {"type": "option", "options": ["Single", "Double"], "config_key": "burning_ship_precision", "default": "Single"},
|
||||||
|
"Escape Radius": {"type": "slider", "min": 1, "max": 10, "config_key": "burning_ship_escape_radius", "default": 2, "step": 0.1},
|
||||||
|
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "burning_ship_zoom_increase", "default": 2},
|
||||||
|
"Max Iterations": {"type": "slider", "min": 100, "max": 10000, "config_key": "burning_ship_max_iter", "default": 200, "step": 100}
|
||||||
|
},
|
||||||
"Sierpinsky Carpet": {
|
"Sierpinsky Carpet": {
|
||||||
"Float Precision": {"type": "option", "options": ["Single", "Double"], "config_key": "sierpinsky_precision", "default": "Single"},
|
"Float Precision": {"type": "option", "options": ["Single", "Double"], "config_key": "sierpinsky_precision", "default": "Single"},
|
||||||
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "sierpinsky_zoom_increase", "default": 2},
|
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "sierpinsky_zoom_increase", "default": 2},
|
||||||
@@ -49,7 +67,11 @@ settings = {
|
|||||||
"N": {"type": "slider", "min": 1, "max": 10, "config_key": "julia_n", "default": 2, "step": 1},
|
"N": {"type": "slider", "min": 1, "max": 10, "config_key": "julia_n", "default": 2, "step": 1},
|
||||||
"Escape Radius": {"type": "slider", "min": 1, "max": 10, "config_key": "julia_escape_radius", "default": 2, "step": 0.1},
|
"Escape Radius": {"type": "slider", "min": 1, "max": 10, "config_key": "julia_escape_radius", "default": 2, "step": 0.1},
|
||||||
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "julia_zoom_increase", "default": 2},
|
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "julia_zoom_increase", "default": 2},
|
||||||
"Max Iterations": {"type": "slider", "min": 100, "max": 10000, "config_key": "julia_max_iter", "default": 200, "step": 100}
|
"Max Iterations": {"type": "slider", "min": 100, "max": 4000, "config_key": "julia_max_iter", "default": 200, "step": 100}
|
||||||
|
},
|
||||||
|
"Newton Fractal": {
|
||||||
|
"Zoom Increase Per Click": {"type": "slider", "min": 2, "max": 100, "config_key": "newton_fractal_zoom_increase", "default": 2},
|
||||||
|
"Max Iterations": {"type": "slider", "min": 100, "max": 10000, "config_key": "newton_fractal_max_iter", "default": 200, "step": 100}
|
||||||
},
|
},
|
||||||
"Graphics": {
|
"Graphics": {
|
||||||
"Window Mode": {"type": "option", "options": ["Windowed", "Fullscreen", "Borderless"], "config_key": "window_mode", "default": "Windowed"},
|
"Window Mode": {"type": "option", "options": ["Windowed", "Fullscreen", "Borderless"], "config_key": "window_mode", "default": "Windowed"},
|
||||||
|
|||||||
Reference in New Issue
Block a user