some new stuff.
idk its all pretty fun! some C++ too!
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -53,6 +53,8 @@ ehthumbs.db
|
||||
# C / C++ / Rust builds (if you use extensions)
|
||||
*.o
|
||||
*.so
|
||||
*.obj
|
||||
*.exe
|
||||
*.a
|
||||
*.out
|
||||
target/
|
||||
|
||||
60
cube/fibonnacci.py
Normal file
60
cube/fibonnacci.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import math
|
||||
import tkinter as tk
|
||||
|
||||
class Fibonacci:
|
||||
def s5(self, n, r):
|
||||
"""Generate Fibonacci spiral polar coordinates"""
|
||||
spirals = []
|
||||
phi = (1 + 5 ** 0.5) / 2 # golden ratio
|
||||
for i in range(n + 1):
|
||||
angle = (i * 360 / phi) % 360
|
||||
spirals.append((r * (i ** 0.5), angle))
|
||||
return spirals
|
||||
|
||||
def pol2cart(self, r, theta):
|
||||
x = r * math.cos(math.radians(theta))
|
||||
y = r * math.sin(math.radians(theta))
|
||||
return x, y
|
||||
|
||||
def calculate_coordinates(self, num_points=200, distance=15):
|
||||
# Convert polar to Cartesian coordinates
|
||||
self.coordinates = [self.pol2cart(r, t) for r, t in self.s5(num_points, distance)]
|
||||
# Center for the canvas
|
||||
self.coordinates = [(x + 250, y + 250) for x, y in self.coordinates]
|
||||
|
||||
def plot_numbers(self, canvas):
|
||||
self.calculate_coordinates()
|
||||
for idx, (x, y) in enumerate(self.coordinates, start=1):
|
||||
canvas.create_oval(x - 7, y - 7, x + 7, y + 7, fill="white")
|
||||
canvas.create_text(x, y, text=str(idx))
|
||||
|
||||
def plot_lines(self, canvas):
|
||||
for delta in [21, 34]:
|
||||
for start in range(34):
|
||||
x0, y0 = self.coordinates[start]
|
||||
i = start + delta
|
||||
while i < len(self.coordinates):
|
||||
x1, y1 = self.coordinates[i]
|
||||
canvas.create_line(x0, y0, x1, y1)
|
||||
x0, y0 = x1, y1
|
||||
i += delta
|
||||
|
||||
def create_gui(self):
|
||||
master = tk.Tk()
|
||||
master.title("Fibonacci Spiral")
|
||||
canvas = tk.Canvas(master, width=500, height=500, bg="white")
|
||||
canvas.pack()
|
||||
|
||||
self.plot_numbers(canvas)
|
||||
self.plot_lines(canvas)
|
||||
|
||||
master.mainloop()
|
||||
|
||||
|
||||
def main():
|
||||
f = Fibonacci()
|
||||
f.create_gui()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
142
cube/main.py
Normal file
142
cube/main.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
from matplotlib.animation import FuncAnimation
|
||||
from matplotlib.widgets import Button
|
||||
|
||||
cube_points = np.array([
|
||||
[-1, -1, -1],
|
||||
[-1, -1, 1],
|
||||
[-1, 1, -1],
|
||||
[-1, 1, 1],
|
||||
[ 1, -1, -1],
|
||||
[ 1, -1, 1],
|
||||
[ 1, 1, -1],
|
||||
[ 1, 1, 1]
|
||||
])
|
||||
|
||||
edges = [
|
||||
(0, 1), (0, 2), (0, 4),
|
||||
(1, 3), (1, 5),
|
||||
(2, 3), (2, 6),
|
||||
(3, 7),
|
||||
(4, 5), (4, 6),
|
||||
(5, 7),
|
||||
(6, 7)
|
||||
]
|
||||
|
||||
def rotation_y(theta):
|
||||
return np.array([
|
||||
[np.cos(theta), 0, np.sin(theta)],
|
||||
[0, 1, 0], # 0,1,0
|
||||
[-np.sin(theta), 0, np.cos(theta)]
|
||||
])
|
||||
|
||||
|
||||
def rotation_z(theta):
|
||||
return np.array([
|
||||
[np.cos(theta), -np.sin(theta), 0],
|
||||
[np.sin(theta), np.cos(theta), 0],
|
||||
[0, 0, 1]
|
||||
])
|
||||
|
||||
|
||||
fig = plt.figure(figsize=(8, 6))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
ax.set_xlim(-2, 2)
|
||||
ax.set_ylim(-2, 2)
|
||||
ax.set_zlim(-2, 2)
|
||||
ax.set_box_aspect([1, 1, 1])
|
||||
ax.set_title("Matrixmultiplikation", pad=20)
|
||||
|
||||
lines = [ax.plot([], [], [], color='red')[0] for _ in edges]
|
||||
text = ax.text2D(1.02, 0.5, "", transform=ax.transAxes,
|
||||
fontsize=10, color='black', family='monospace', va='center')
|
||||
|
||||
paused = False
|
||||
highlight = False
|
||||
points_scat = None
|
||||
labels = []
|
||||
|
||||
|
||||
def update(frame):
|
||||
global points_scat, labels
|
||||
|
||||
if paused:
|
||||
return lines + [text]
|
||||
|
||||
theta = np.radians(frame)
|
||||
R_y = rotation_y(theta)
|
||||
R_z = rotation_z(theta * 0.7)
|
||||
R = R_y @ R_z
|
||||
|
||||
rotated = cube_points @ R.T
|
||||
|
||||
# Update edges
|
||||
for line, (i1, i2) in zip(lines, edges):
|
||||
p1, p2 = rotated[i1], rotated[i2]
|
||||
line.set_data([p1[0], p2[0]], [p1[1], p2[1]])
|
||||
line.set_3d_properties([p1[2], p2[2]])
|
||||
|
||||
# Update rotation matrices display
|
||||
matrix_str_y = "\n".join(
|
||||
["[" + " ".join(f"{val:+.2f}" for val in row) + "]" for row in R_y]
|
||||
)
|
||||
matrix_str_z = "\n".join(
|
||||
["[" + " ".join(f"{val:+.2f}" for val in row) + "]" for row in R_z]
|
||||
)
|
||||
matrix_str_R = "\n".join(
|
||||
["[" + " ".join(f"{val:+.2f}" for val in row) + "]" for row in R]
|
||||
)
|
||||
|
||||
text.set_text(
|
||||
f"θ = {np.degrees(theta):6.2f}°\n"
|
||||
f"sin(θ) = {np.sin(theta): .3f}\n"
|
||||
f"cos(θ) = {np.cos(theta): .3f}\n\n"
|
||||
f"R_y(θ):\n{matrix_str_y}\n\n"
|
||||
f"R_z(0.7θ):\n{matrix_str_z}\n\n"
|
||||
f"R = R_y · R_z:\n{matrix_str_R}"
|
||||
)
|
||||
|
||||
# Always show vertex points and labels
|
||||
if points_scat is None:
|
||||
points_scat = ax.scatter([], [], [], color='blue', s=40)
|
||||
|
||||
points_scat._offsets3d = (
|
||||
rotated[:, 0], rotated[:, 1], rotated[:, 2]
|
||||
)
|
||||
|
||||
for label in labels:
|
||||
label.remove()
|
||||
labels.clear()
|
||||
|
||||
for i, p in enumerate(rotated):
|
||||
labels.append(ax.text(p[0], p[1], p[2], f"P{i}",
|
||||
color='black', fontsize=8))
|
||||
|
||||
return lines + [text]
|
||||
|
||||
|
||||
|
||||
axpause = plt.axes([0.4, 0.02, 0.3, 0.05])
|
||||
bpause = Button(axpause, 'Pause / Resume')
|
||||
|
||||
|
||||
def toggle_pause(event):
|
||||
global paused
|
||||
paused = not paused
|
||||
|
||||
|
||||
def toggle_highlight(event):
|
||||
global highlight
|
||||
highlight = not highlight
|
||||
|
||||
|
||||
bpause.on_clicked(toggle_pause)
|
||||
|
||||
ani = FuncAnimation(
|
||||
fig, update, frames=np.arange(0, 360, 2),
|
||||
interval=50, blit=False
|
||||
)
|
||||
|
||||
plt.show()
|
||||
52
cube/pattern.py
Normal file
52
cube/pattern.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import pygame
|
||||
import math
|
||||
import sys
|
||||
|
||||
# --- Pygame setup ---
|
||||
pygame.init()
|
||||
WIDTH, HEIGHT = 800, 800
|
||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
# --- Colors ---
|
||||
BLACK = (0, 0, 0)
|
||||
colors = [(255, 100, 100), (100, 255, 150), (100, 150, 255), (255, 255, 100)]
|
||||
|
||||
# --- Fibonacci sequence generator ---
|
||||
def fibonacci(n):
|
||||
fibs = [0, 1]
|
||||
for i in range(2, n):
|
||||
fibs.append(fibs[-1] + fibs[-2])
|
||||
return fibs
|
||||
|
||||
# --- Spiral drawing ---
|
||||
def draw_fibonacci_spiral(n, angle_offset):
|
||||
fibs = fibonacci(n)
|
||||
cx, cy = WIDTH//2, HEIGHT//2
|
||||
scale = 0.05 # scale down Fibonacci numbers to fit screen
|
||||
|
||||
for i, f in enumerate(fibs[1:], 1): # skip the first 0
|
||||
angle = i * 137.5 + angle_offset # golden angle in degrees
|
||||
rad = math.radians(angle)
|
||||
x = cx + f * math.cos(rad) * scale
|
||||
y = cy + f * math.sin(rad) * scale
|
||||
color = colors[i % len(colors)]
|
||||
pygame.draw.circle(screen, color, (int(x), int(y)), max(int(f*scale*0.5)+2, 2))
|
||||
|
||||
# --- Main loop ---
|
||||
angle_offset = 0
|
||||
running = True
|
||||
while running:
|
||||
screen.fill(BLACK)
|
||||
draw_fibonacci_spiral(50, angle_offset)
|
||||
angle_offset += 1 # rotate the spiral over time
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
|
||||
pygame.display.flip()
|
||||
clock.tick(60)
|
||||
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
173
cube/tktcl.py
Normal file
173
cube/tktcl.py
Normal file
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
rotating_cube_tk.py
|
||||
|
||||
Simple Tkinter app that rotates a 3D cube using rotation matrices along X and Z axes.
|
||||
No dependencies outside the Python standard library.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
import math
|
||||
import time
|
||||
|
||||
# Canvas size
|
||||
W, H = 700, 700
|
||||
|
||||
# Cube definition (8 vertices of a cube centered at origin)
|
||||
size = 150
|
||||
vertices = [
|
||||
(-1, -1, -1),
|
||||
(-1, -1, 1),
|
||||
(-1, 1, -1),
|
||||
(-1, 1, 1),
|
||||
( 1, -1, -1),
|
||||
( 1, -1, 1),
|
||||
( 1, 1, -1),
|
||||
( 1, 1, 1),
|
||||
]
|
||||
# Scale vertices by size
|
||||
vertices = [(x * size, y * size, z * size) for (x, y, z) in vertices]
|
||||
|
||||
# Edges connecting vertices (pairs of indices)
|
||||
edges = [
|
||||
(0,1), (0,2), (0,4),
|
||||
(1,3), (1,5),
|
||||
(2,3), (2,6),
|
||||
(3,7),
|
||||
(4,5), (4,6),
|
||||
(5,7),
|
||||
(6,7),
|
||||
]
|
||||
|
||||
# Rotation speeds (radians per frame)
|
||||
rot_speed_x = 0.02
|
||||
rot_speed_z = 0.015
|
||||
|
||||
# Perspective parameters
|
||||
viewer_distance = 600 # Larger -> weaker perspective
|
||||
fov = 500 # Field of view scaling
|
||||
|
||||
class CubeApp:
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
master.title("3D Rotating Cube (X and Z rotation matrices)")
|
||||
|
||||
self.canvas = tk.Canvas(master, width=W, height=H, bg="white")
|
||||
self.canvas.pack(fill="both", expand=True)
|
||||
|
||||
# angles
|
||||
self.ang_x = 0.0
|
||||
self.ang_z = 0.0
|
||||
|
||||
# control
|
||||
self.paused = False
|
||||
self.last_time = time.time()
|
||||
|
||||
# drawn items (to update instead of recreating shapes each frame)
|
||||
self.line_ids = []
|
||||
for _ in edges:
|
||||
self.line_ids.append(self.canvas.create_line(0,0,0,0, width=2))
|
||||
|
||||
# instructions text
|
||||
self.canvas.create_text(10, 10, anchor="nw",
|
||||
text="Space: pause/resume Up/Down: speed X Left/Right: speed Z",
|
||||
fill="black", font=("Helvetica", 10))
|
||||
|
||||
# Bind keys
|
||||
master.bind("<space>", self.toggle_pause)
|
||||
master.bind("<Up>", self.speed_up_x)
|
||||
master.bind("<Down>", self.speed_down_x)
|
||||
master.bind("<Right>", self.speed_up_z)
|
||||
master.bind("<Left>", self.speed_down_z)
|
||||
|
||||
# Start animation
|
||||
self.animate()
|
||||
|
||||
# Rotation matrix around X for angle a
|
||||
def rotate_x(self, point, a):
|
||||
x, y, z = point
|
||||
cos_a = math.cos(a)
|
||||
sin_a = math.sin(a)
|
||||
y2 = y * cos_a - z * sin_a
|
||||
z2 = y * sin_a + z * cos_a
|
||||
return (x, y2, z2)
|
||||
|
||||
# Rotation matrix around Z for angle a
|
||||
def rotate_z(self, point, a):
|
||||
x, y, z = point
|
||||
cos_a = math.cos(a)
|
||||
sin_a = math.sin(a)
|
||||
x2 = x * cos_a - y * sin_a
|
||||
y2 = x * sin_a + y * cos_a
|
||||
return (x2, y2, z)
|
||||
|
||||
# Project 3D point to 2D using simple perspective
|
||||
def project(self, point):
|
||||
x, y, z = point
|
||||
# shift z relative to viewer so we don't divide by zero
|
||||
z_shifted = z + viewer_distance
|
||||
if z_shifted == 0:
|
||||
z_shifted = 0.0001
|
||||
factor = fov / z_shifted
|
||||
x_proj = x * factor + W/2
|
||||
y_proj = -y * factor + H/2 # invert y for screen coords
|
||||
return (x_proj, y_proj)
|
||||
|
||||
def animate(self):
|
||||
# compute time delta for smoother animation (in case of slow frame)
|
||||
now = time.time()
|
||||
dt = now - self.last_time
|
||||
self.last_time = now
|
||||
|
||||
if not self.paused:
|
||||
# update angles (scale by dt to be time-based)
|
||||
self.ang_x += rot_speed_x * (dt * 60) # adjust to feel like frame-based speeds
|
||||
self.ang_z += rot_speed_z * (dt * 60)
|
||||
|
||||
# compute rotated points
|
||||
rotated = []
|
||||
for v in vertices:
|
||||
r = self.rotate_x(v, self.ang_x)
|
||||
r = self.rotate_z(r, self.ang_z)
|
||||
rotated.append(r)
|
||||
|
||||
# project all points
|
||||
projected = [self.project(p) for p in rotated]
|
||||
|
||||
# draw edges by updating canvas lines
|
||||
for i, (a, b) in enumerate(edges):
|
||||
x1, y1 = projected[a]
|
||||
x2, y2 = projected[b]
|
||||
# update existing line coordinates
|
||||
self.canvas.coords(self.line_ids[i], x1, y1, x2, y2)
|
||||
|
||||
# optionally: draw small circles at vertices (commented out to keep it clean)
|
||||
for (x, y) in projected:
|
||||
self.canvas.create_oval(x-3, y-3, x+3, y+3, fill="black")
|
||||
|
||||
# schedule next frame (aiming ~60 FPS)
|
||||
self.master.after(1, self.animate)
|
||||
|
||||
# Controls
|
||||
def toggle_pause(self, event=None):
|
||||
self.paused = not self.paused
|
||||
|
||||
def speed_up_x(self, event=None):
|
||||
global rot_speed_x
|
||||
rot_speed_x += 0.005
|
||||
|
||||
def speed_down_x(self, event=None):
|
||||
global rot_speed_x
|
||||
rot_speed_x -= 0.005
|
||||
|
||||
def speed_up_z(self, event=None):
|
||||
global rot_speed_z
|
||||
rot_speed_z += 0.005
|
||||
|
||||
def speed_down_z(self, event=None):
|
||||
global rot_speed_z
|
||||
rot_speed_z -= 0.005
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = CubeApp(root)
|
||||
root.mainloop()
|
||||
199
simulations/astar/main.py
Normal file
199
simulations/astar/main.py
Normal file
@@ -0,0 +1,199 @@
|
||||
import pygame
|
||||
import random
|
||||
from queue import PriorityQueue
|
||||
import time
|
||||
|
||||
WIDTH = 600
|
||||
ROWS = 20
|
||||
CELL_SIZE = WIDTH // ROWS
|
||||
FPS = 60
|
||||
DELAY = 0
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
GREEN = (0, 255, 0)
|
||||
RED = (255, 0, 0)
|
||||
BLUE = (0, 0, 255)
|
||||
GRAY = (200, 200, 200)
|
||||
YELLOW = (255, 255, 0)
|
||||
|
||||
class Cell:
|
||||
def __init__(self, row, col):
|
||||
self.row = row
|
||||
self.col = col
|
||||
self.x = col * CELL_SIZE
|
||||
self.y = row * CELL_SIZE
|
||||
self.walls = [True, True, True, True] # top, right, bottom, left
|
||||
self.visited = False
|
||||
|
||||
def draw(self, win):
|
||||
if self.visited:
|
||||
pygame.draw.rect(win, WHITE, (self.x, self.y, CELL_SIZE, CELL_SIZE))
|
||||
if self.walls[0]: pygame.draw.line(win, BLACK, (self.x, self.y), (self.x+CELL_SIZE, self.y), 2)
|
||||
if self.walls[1]: pygame.draw.line(win, BLACK, (self.x+CELL_SIZE, self.y), (self.x+CELL_SIZE, self.y+CELL_SIZE), 2)
|
||||
if self.walls[2]: pygame.draw.line(win, BLACK, (self.x+CELL_SIZE, self.y+CELL_SIZE), (self.x, self.y+CELL_SIZE), 2)
|
||||
if self.walls[3]: pygame.draw.line(win, BLACK, (self.x, self.y+CELL_SIZE), (self.x, self.y), 2)
|
||||
|
||||
def highlight(self, win, color):
|
||||
pygame.draw.rect(win, color, (self.x, self.y, CELL_SIZE, CELL_SIZE))
|
||||
|
||||
|
||||
def generate_maze(grid, current):
|
||||
stack = []
|
||||
current.visited = True
|
||||
|
||||
while True:
|
||||
neighbors = []
|
||||
r, c = current.row, current.col
|
||||
if r > 0 and not grid[r-1][c].visited: neighbors.append(grid[r-1][c])
|
||||
if r < ROWS-1 and not grid[r+1][c].visited: neighbors.append(grid[r+1][c])
|
||||
if c > 0 and not grid[r][c-1].visited: neighbors.append(grid[r][c-1])
|
||||
if c < ROWS-1 and not grid[r][c+1].visited: neighbors.append(grid[r][c+1])
|
||||
|
||||
if neighbors:
|
||||
next_cell = random.choice(neighbors)
|
||||
stack.append(current)
|
||||
remove_walls(current, next_cell)
|
||||
next_cell.visited = True
|
||||
current = next_cell
|
||||
elif stack:
|
||||
current = stack.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
def remove_walls(a, b):
|
||||
dx = a.col - b.col
|
||||
dy = a.row - b.row
|
||||
if dx == 1:
|
||||
a.walls[3] = False
|
||||
b.walls[1] = False
|
||||
elif dx == -1:
|
||||
a.walls[1] = False
|
||||
b.walls[3] = False
|
||||
if dy == 1:
|
||||
a.walls[0] = False
|
||||
b.walls[2] = False
|
||||
elif dy == -1:
|
||||
a.walls[2] = False
|
||||
b.walls[0] = False
|
||||
|
||||
def heuristic(a, b):
|
||||
return abs(a.row - b.row) + abs(a.col - b.col)
|
||||
|
||||
def astar(grid, start, end, win):
|
||||
count = 0
|
||||
open_set = PriorityQueue()
|
||||
open_set.put((0, count, start))
|
||||
came_from = {}
|
||||
g_score = {cell: float("inf") for row in grid for cell in row}
|
||||
g_score[start] = 0
|
||||
f_score = {cell: float("inf") for row in grid for cell in row}
|
||||
f_score[start] = heuristic(start, end)
|
||||
open_set_hash = {start}
|
||||
|
||||
while not open_set.empty():
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
return []
|
||||
|
||||
current = open_set.get()[2]
|
||||
open_set_hash.remove(current)
|
||||
|
||||
if current == end:
|
||||
path = []
|
||||
while current in came_from:
|
||||
current = came_from[current]
|
||||
path.append(current)
|
||||
return path
|
||||
|
||||
for neighbor in get_neighbors(grid, current):
|
||||
temp_g = g_score[current] + 1
|
||||
if temp_g < g_score[neighbor]:
|
||||
came_from[neighbor] = current
|
||||
g_score[neighbor] = temp_g
|
||||
f_score[neighbor] = temp_g + heuristic(neighbor, end)
|
||||
if neighbor not in open_set_hash:
|
||||
count += 1
|
||||
open_set.put((f_score[neighbor], count, neighbor))
|
||||
open_set_hash.add(neighbor)
|
||||
|
||||
# visualize
|
||||
draw_grid(win, grid)
|
||||
for cell in open_set_hash:
|
||||
cell.highlight(win, GREEN)
|
||||
pygame.display.update()
|
||||
time.sleep(DELAY)
|
||||
|
||||
return []
|
||||
|
||||
def get_neighbors(grid, cell):
|
||||
neighbors = []
|
||||
r, c = cell.row, cell.col
|
||||
if not cell.walls[0] and r > 0: neighbors.append(grid[r-1][c])
|
||||
if not cell.walls[1] and c < ROWS-1: neighbors.append(grid[r][c+1])
|
||||
if not cell.walls[2] and r < ROWS-1: neighbors.append(grid[r+1][c])
|
||||
if not cell.walls[3] and c > 0: neighbors.append(grid[r][c-1])
|
||||
return neighbors
|
||||
|
||||
# --- DRAWING ---
|
||||
def draw_grid(win, grid):
|
||||
win.fill(WHITE)
|
||||
for row in grid:
|
||||
for cell in row:
|
||||
cell.draw(win)
|
||||
|
||||
# --- MAIN ---
|
||||
def main():
|
||||
global ROWS, CELL_SIZE
|
||||
pygame.init()
|
||||
win = pygame.display.set_mode((WIDTH, WIDTH))
|
||||
pygame.display.set_caption("A*")
|
||||
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
def create_maze(rows):
|
||||
global CELL_SIZE
|
||||
CELL_SIZE = WIDTH // rows
|
||||
grid = [[Cell(r, c) for c in range(rows)] for r in range(rows)]
|
||||
generate_maze(grid, grid[0][0])
|
||||
return grid
|
||||
|
||||
grid = create_maze(ROWS)
|
||||
start = grid[0][0]
|
||||
end = grid[ROWS-1][ROWS-1]
|
||||
path = []
|
||||
|
||||
running = True
|
||||
while running:
|
||||
clock.tick(FPS)
|
||||
draw_grid(win, grid)
|
||||
start.highlight(win, BLUE)
|
||||
end.highlight(win, RED)
|
||||
for cell in path:
|
||||
cell.highlight(win, YELLOW)
|
||||
pygame.display.update()
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_SPACE:
|
||||
path = astar(grid, start, end, win)
|
||||
if event.key == pygame.K_UP:
|
||||
ROWS += 1
|
||||
grid = create_maze(ROWS)
|
||||
start = grid[0][0]
|
||||
end = grid[ROWS-1][ROWS-1]
|
||||
path = []
|
||||
if event.key == pygame.K_DOWN and ROWS > 5:
|
||||
ROWS -= 1
|
||||
grid = create_maze(ROWS)
|
||||
start = grid[0][0]
|
||||
end = grid[ROWS-1][ROWS-1]
|
||||
path = []
|
||||
|
||||
pygame.quit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
731
simulations/balls/bowling.py
Normal file
731
simulations/balls/bowling.py
Normal file
@@ -0,0 +1,731 @@
|
||||
import pygame
|
||||
from pygame.locals import *
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GLU import *
|
||||
import math
|
||||
import random
|
||||
import numpy as np
|
||||
|
||||
class Ball:
|
||||
def __init__(self):
|
||||
self.pos = [0, 0.5, 15]
|
||||
self.vel = [0, 0, 0]
|
||||
self.rot = [0, 0, 0]
|
||||
self.rot_vel = [0, 0, 0]
|
||||
self.radius = 0.5
|
||||
self.mass = 7.0
|
||||
self.launched = False
|
||||
self.finger_holes = []
|
||||
self.texture = None
|
||||
self.load_texture()
|
||||
|
||||
def load_texture(self):
|
||||
# Create a simple procedural texture for the ball
|
||||
texture_size = 64
|
||||
texture_data = np.zeros((texture_size, texture_size, 3), dtype=np.uint8)
|
||||
|
||||
for i in range(texture_size):
|
||||
for j in range(texture_size):
|
||||
# Create a marble-like pattern
|
||||
x = i / texture_size * 2 * math.pi
|
||||
y = j / texture_size * 2 * math.pi
|
||||
|
||||
# Base color (dark blue/black)
|
||||
r = 20 + int(10 * math.sin(x * 5) * math.cos(y * 5))
|
||||
g = 20 + int(10 * math.sin(x * 3) * math.cos(y * 7))
|
||||
b = 60 + int(20 * math.sin(x * 7) * math.cos(y * 3))
|
||||
|
||||
texture_data[i, j] = [r, g, b]
|
||||
|
||||
self.texture = glGenTextures(1)
|
||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture_size, texture_size, 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, texture_data)
|
||||
|
||||
def reset(self):
|
||||
self.pos = [0, 0.5, 15]
|
||||
self.vel = [0, 0, 0]
|
||||
self.rot = [0, 0, 0]
|
||||
self.rot_vel = [0, 0, 0]
|
||||
self.launched = False
|
||||
|
||||
def launch(self, power, angle):
|
||||
self.launched = True
|
||||
self.vel[2] = -power * 20 # Increased power
|
||||
self.vel[0] = math.sin(math.radians(angle)) * power * 8
|
||||
self.rot_vel[0] = -power * 300 # More realistic spin
|
||||
|
||||
def update(self, dt, pins):
|
||||
if not self.launched:
|
||||
return False
|
||||
|
||||
# Gravity
|
||||
self.vel[1] -= 9.8 * dt
|
||||
|
||||
# Floor collision with improved physics
|
||||
if self.pos[1] - self.radius < 0:
|
||||
self.pos[1] = self.radius
|
||||
# Energy loss on bounce
|
||||
self.vel[1] = -self.vel[1] * 0.6 # Bounce with energy loss
|
||||
|
||||
# Improved friction based on surface contact
|
||||
friction = 0.95
|
||||
self.vel[0] *= friction
|
||||
self.vel[2] *= friction
|
||||
|
||||
# Add slight random variation for more realistic movement
|
||||
self.vel[0] += random.uniform(-0.1, 0.1)
|
||||
self.vel[2] += random.uniform(-0.1, 0.1)
|
||||
|
||||
# Air resistance
|
||||
air_resistance = 0.99
|
||||
self.vel[0] *= air_resistance
|
||||
self.vel[2] *= air_resistance
|
||||
|
||||
# Update position
|
||||
for i in range(3):
|
||||
self.pos[i] += self.vel[i] * dt
|
||||
self.rot[i] += self.rot_vel[i] * dt
|
||||
|
||||
# Check collisions with pins
|
||||
for pin in pins:
|
||||
if pin.standing:
|
||||
dx = self.pos[0] - pin.pos[0]
|
||||
dz = self.pos[2] - pin.pos[2]
|
||||
dist = math.sqrt(dx*dx + dz*dz)
|
||||
|
||||
if dist < self.radius + pin.radius:
|
||||
pin.knock_down(self.vel)
|
||||
# More realistic bounce physics
|
||||
normal = [dx/dist, 0, dz/dist]
|
||||
dot = self.vel[0]*normal[0] + self.vel[2]*normal[2]
|
||||
self.vel[0] -= 1.8 * dot * normal[0]
|
||||
self.vel[2] -= 1.8 * dot * normal[2]
|
||||
|
||||
# Add some vertical bounce
|
||||
self.vel[1] += 0.5
|
||||
|
||||
# Check if ball is out of bounds
|
||||
if self.pos[2] < -5 or abs(self.pos[0]) > 10:
|
||||
return True
|
||||
|
||||
# Check if ball has stopped
|
||||
speed = math.sqrt(self.vel[0]**2 + self.vel[1]**2 + self.vel[2]**2)
|
||||
if speed < 0.1 and self.pos[1] <= self.radius + 0.01:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def draw(self):
|
||||
glPushMatrix()
|
||||
glTranslatef(*self.pos)
|
||||
glRotatef(self.rot[0], 1, 0, 0)
|
||||
glRotatef(self.rot[1], 0, 1, 0)
|
||||
|
||||
# Enable texturing for the ball
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glBindTexture(GL_TEXTURE_2D, self.texture)
|
||||
|
||||
# Ball body with texture
|
||||
glColor3f(0.8, 0.8, 1.0) # Lighter base color
|
||||
quad = gluNewQuadric()
|
||||
gluQuadricTexture(quad, GL_TRUE)
|
||||
gluSphere(quad, self.radius, 32, 32)
|
||||
|
||||
# Finger holes
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glColor3f(0.05, 0.05, 0.05)
|
||||
for angle in [0, 120, 240]:
|
||||
glPushMatrix()
|
||||
glRotatef(angle, 0, 1, 0)
|
||||
glTranslatef(0.3, 0, 0)
|
||||
gluCylinder(quad, 0.06, 0.06, 0.2, 16, 1)
|
||||
glPopMatrix()
|
||||
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glPopMatrix()
|
||||
|
||||
class Pin:
|
||||
def __init__(self, x, z):
|
||||
self.pos = [x, 0.75, z]
|
||||
self.vel = [0, 0, 0]
|
||||
self.rot = [0, 0, 0]
|
||||
self.rot_vel = [0, 0, 0]
|
||||
self.radius = 0.15
|
||||
self.height = 0.75
|
||||
self.standing = True
|
||||
self.fall_timer = 0
|
||||
self.initial_pos = [x, 0.75, z] # Store initial position for reset
|
||||
|
||||
def knock_down(self, impact_vel):
|
||||
if self.standing:
|
||||
self.standing = False
|
||||
# More realistic pin physics based on impact
|
||||
self.vel = [
|
||||
impact_vel[0] * random.uniform(0.3, 0.7),
|
||||
random.uniform(1.5, 3.0),
|
||||
impact_vel[2] * random.uniform(0.3, 0.7)
|
||||
]
|
||||
self.rot_vel = [
|
||||
random.uniform(-400, 400),
|
||||
random.uniform(-400, 400),
|
||||
random.uniform(-400, 400)
|
||||
]
|
||||
|
||||
def reset(self):
|
||||
self.pos = self.initial_pos.copy()
|
||||
self.vel = [0, 0, 0]
|
||||
self.rot = [0, 0, 0]
|
||||
self.rot_vel = [0, 0, 0]
|
||||
self.standing = True
|
||||
self.fall_timer = 0
|
||||
|
||||
def update(self, dt):
|
||||
if not self.standing:
|
||||
# Gravity
|
||||
self.vel[1] -= 9.8 * dt
|
||||
|
||||
# Floor collision with improved physics
|
||||
if self.pos[1] < 0.1:
|
||||
self.pos[1] = 0.1
|
||||
self.vel[1] = -self.vel[1] * 0.3 # Bounce with energy loss
|
||||
self.vel[0] *= 0.7
|
||||
self.vel[2] *= 0.7
|
||||
self.rot_vel = [v * 0.7 for v in self.rot_vel]
|
||||
|
||||
# Update position
|
||||
for i in range(3):
|
||||
self.pos[i] += self.vel[i] * dt
|
||||
self.rot[i] += self.rot_vel[i] * dt
|
||||
|
||||
self.fall_timer += dt
|
||||
|
||||
def draw(self):
|
||||
glPushMatrix()
|
||||
glTranslatef(*self.pos)
|
||||
|
||||
if not self.standing:
|
||||
glRotatef(self.rot[0], 1, 0, 0)
|
||||
glRotatef(self.rot[1], 0, 1, 0)
|
||||
glRotatef(self.rot[2], 0, 0, 1)
|
||||
|
||||
# Pin body with improved materials
|
||||
glColor3f(0.95, 0.95, 0.95) # Brighter white
|
||||
quad = gluNewQuadric()
|
||||
gluCylinder(quad, 0.15, 0.08, self.height, 16, 1)
|
||||
|
||||
# Red stripes with gloss effect
|
||||
glColor3f(0.9, 0.1, 0.1)
|
||||
glPushMatrix()
|
||||
glTranslatef(0, 0, self.height * 0.6)
|
||||
gluCylinder(quad, 0.11, 0.09, self.height * 0.1, 16, 1)
|
||||
glPopMatrix()
|
||||
|
||||
# Second stripe
|
||||
glPushMatrix()
|
||||
glTranslatef(0, 0, self.height * 0.3)
|
||||
gluCylinder(quad, 0.12, 0.11, self.height * 0.1, 16, 1)
|
||||
glPopMatrix()
|
||||
|
||||
# Top with rounded cap
|
||||
glPushMatrix()
|
||||
glTranslatef(0, 0, self.height)
|
||||
gluSphere(quad, 0.08, 16, 16)
|
||||
glPopMatrix()
|
||||
|
||||
glPopMatrix()
|
||||
|
||||
class BowlingGame:
|
||||
def __init__(self):
|
||||
pygame.init()
|
||||
self.width, self.height = 1200, 800
|
||||
self.screen = pygame.display.set_mode((self.width, self.height), DOUBLEBUF | OPENGL)
|
||||
pygame.display.set_caption("Enhanced 3D Bowling Game")
|
||||
|
||||
self.setup_gl()
|
||||
|
||||
self.ball = Ball()
|
||||
self.pins = self.setup_pins()
|
||||
|
||||
self.power = 0
|
||||
self.angle = 0
|
||||
self.charging = False
|
||||
self.max_power = 1.0
|
||||
|
||||
self.score = 0
|
||||
self.frame = 1
|
||||
self.roll = 1
|
||||
self.game_over = False
|
||||
|
||||
self.camera_pos = [0, 5, 20]
|
||||
self.camera_target = [0, 1, 0]
|
||||
self.camera_angle = 0
|
||||
self.camera_distance = 20
|
||||
|
||||
self.lane_texture = self.create_lane_texture()
|
||||
self.background_color = [0.1, 0.2, 0.4] # Dark blue background
|
||||
|
||||
# Game state
|
||||
self.waiting_for_roll = True
|
||||
self.ball_done = False
|
||||
self.knocked_this_roll = 0
|
||||
|
||||
# Lighting
|
||||
self.light_pos = [5, 10, 10]
|
||||
self.ambient_light = [0.4, 0.4, 0.4, 1.0]
|
||||
self.diffuse_light = [0.9, 0.9, 0.9, 1.0]
|
||||
self.specular_light = [1.0, 1.0, 1.0, 1.0]
|
||||
|
||||
def create_lane_texture(self):
|
||||
texture_size = 256
|
||||
texture_data = np.zeros((texture_size, texture_size, 3), dtype=np.uint8)
|
||||
|
||||
for i in range(texture_size):
|
||||
for j in range(texture_size):
|
||||
# Create wood grain pattern
|
||||
wood_intensity = 150 + int(50 * math.sin(i/20) * math.cos(j/30))
|
||||
texture_data[i, j] = [wood_intensity, wood_intensity-20, wood_intensity-40]
|
||||
|
||||
texture = glGenTextures(1)
|
||||
glBindTexture(GL_TEXTURE_2D, texture)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture_size, texture_size, 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, texture_data)
|
||||
return texture
|
||||
|
||||
def setup_gl(self):
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glEnable(GL_LIGHTING)
|
||||
glEnable(GL_LIGHT0)
|
||||
glEnable(GL_LIGHT1) # Additional light
|
||||
glEnable(GL_COLOR_MATERIAL)
|
||||
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
|
||||
|
||||
# Improved lighting
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, (5, 10, 10, 1))
|
||||
glLightfv(GL_LIGHT0, GL_AMBIENT, (0.4, 0.4, 0.4, 1))
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, (0.9, 0.9, 0.9, 1))
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, (1.0, 1.0, 1.0, 1))
|
||||
|
||||
# Second light for better illumination
|
||||
glLightfv(GL_LIGHT1, GL_POSITION, (-5, 8, 5, 1))
|
||||
glLightfv(GL_LIGHT1, GL_AMBIENT, (0.2, 0.2, 0.2, 1))
|
||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, (0.5, 0.5, 0.5, 1))
|
||||
glLightfv(GL_LIGHT1, GL_SPECULAR, (0.5, 0.5, 0.5, 1))
|
||||
|
||||
# Material properties for better reflections
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0])
|
||||
glMaterialf(GL_FRONT, GL_SHININESS, 50.0)
|
||||
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
gluPerspective(45, self.width/self.height, 0.1, 100.0)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
|
||||
def setup_pins(self):
|
||||
pins = []
|
||||
start_z = -10
|
||||
spacing = 0.5
|
||||
|
||||
# Standard 10-pin triangle formation
|
||||
for row in range(4):
|
||||
for col in range(row + 1):
|
||||
x = (col - row/2) * spacing
|
||||
z = start_z - row * spacing * 0.866
|
||||
pins.append(Pin(x, z))
|
||||
|
||||
return pins
|
||||
|
||||
def reset_pins(self):
|
||||
for pin in self.pins:
|
||||
pin.reset()
|
||||
|
||||
def reset_ball(self):
|
||||
self.ball.reset()
|
||||
self.power = 0
|
||||
self.angle = 0
|
||||
self.charging = False
|
||||
self.ball_done = False
|
||||
self.waiting_for_roll = True
|
||||
|
||||
def count_knocked_pins(self):
|
||||
return sum(1 for pin in self.pins if not pin.standing)
|
||||
|
||||
def draw_lane(self):
|
||||
# Enable texturing for the lane
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glBindTexture(GL_TEXTURE_2D, self.lane_texture)
|
||||
|
||||
# Lane with texture
|
||||
glColor3f(0.9, 0.8, 0.6) # Wood color
|
||||
glBegin(GL_QUADS)
|
||||
glTexCoord2f(0, 0)
|
||||
glVertex3f(-2, 0, 20)
|
||||
glTexCoord2f(1, 0)
|
||||
glVertex3f(2, 0, 20)
|
||||
glTexCoord2f(1, 1)
|
||||
glVertex3f(2, 0, -15)
|
||||
glTexCoord2f(0, 1)
|
||||
glVertex3f(-2, 0, -15)
|
||||
glEnd()
|
||||
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
|
||||
# Gutters with improved appearance
|
||||
glColor3f(0.3, 0.3, 0.3)
|
||||
glBegin(GL_QUADS)
|
||||
# Left gutter
|
||||
glVertex3f(-2.5, 0, 20)
|
||||
glVertex3f(-2, 0, 20)
|
||||
glVertex3f(-2, 0, -15)
|
||||
glVertex3f(-2.5, 0, -15)
|
||||
# Right gutter
|
||||
glVertex3f(2, 0, 20)
|
||||
glVertex3f(2.5, 0, 20)
|
||||
glVertex3f(2.5, 0, -15)
|
||||
glVertex3f(2, 0, -15)
|
||||
glEnd()
|
||||
|
||||
# Gutter edges
|
||||
glColor3f(0.5, 0.5, 0.5)
|
||||
glLineWidth(2.0)
|
||||
glBegin(GL_LINES)
|
||||
# Left edge
|
||||
glVertex3f(-2.5, 0.1, 20)
|
||||
glVertex3f(-2.5, 0.1, -15)
|
||||
# Right edge
|
||||
glVertex3f(2.5, 0.1, 20)
|
||||
glVertex3f(2.5, 0.1, -15)
|
||||
glEnd()
|
||||
|
||||
# Lane boundaries
|
||||
glColor3f(0.9, 0.9, 0.1) # Bright yellow
|
||||
glLineWidth(3.0)
|
||||
glBegin(GL_LINES)
|
||||
# Left boundary
|
||||
glVertex3f(-2, 0.01, 20)
|
||||
glVertex3f(-2, 0.01, -15)
|
||||
# Right boundary
|
||||
glVertex3f(2, 0.01, 20)
|
||||
glVertex3f(2, 0.01, -15)
|
||||
glEnd()
|
||||
|
||||
# Arrows with improved visibility
|
||||
glColor3f(0.7, 0.7, 0.2)
|
||||
for z in range(5, 15, 3):
|
||||
glBegin(GL_TRIANGLES)
|
||||
glVertex3f(0, 0.01, z)
|
||||
glVertex3f(-0.2, 0.01, z + 0.5)
|
||||
glVertex3f(0.2, 0.01, z + 0.5)
|
||||
glEnd()
|
||||
|
||||
# Foul line
|
||||
glColor3f(1.0, 0.0, 0.0) # Red
|
||||
glLineWidth(2.0)
|
||||
glBegin(GL_LINES)
|
||||
glVertex3f(-2, 0.02, 0)
|
||||
glVertex3f(2, 0.02, 0)
|
||||
glEnd()
|
||||
|
||||
glEnable(GL_LIGHTING)
|
||||
|
||||
def draw_hud(self):
|
||||
glDisable(GL_LIGHTING)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
glOrtho(0, self.width, self.height, 0, -1, 1)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
|
||||
# Semi-transparent HUD background
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glColor4f(0.1, 0.1, 0.2, 0.7)
|
||||
glBegin(GL_QUADS)
|
||||
glVertex2f(0, 0)
|
||||
glVertex2f(self.width, 0)
|
||||
glVertex2f(self.width, 80)
|
||||
glVertex2f(0, 80)
|
||||
glEnd()
|
||||
glDisable(GL_BLEND)
|
||||
|
||||
# Score display
|
||||
font = pygame.font.SysFont('Arial', 24)
|
||||
score_text = font.render(f"Score: {self.score} | Frame: {self.frame} | Roll: {self.roll}", True, (255, 255, 255))
|
||||
text_surface = pygame.image.tostring(score_text, 'RGBA', True)
|
||||
glRasterPos2f(20, 20)
|
||||
glDrawPixels(score_text.get_width(), score_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
# Power bar with improved visuals
|
||||
if not self.ball.launched and self.waiting_for_roll:
|
||||
bar_width = 200
|
||||
bar_height = 25
|
||||
x, y = 20, self.height - 60
|
||||
|
||||
# Background
|
||||
glColor3f(0.3, 0.3, 0.3)
|
||||
glBegin(GL_QUADS)
|
||||
glVertex2f(x, y)
|
||||
glVertex2f(x + bar_width, y)
|
||||
glVertex2f(x + bar_width, y + bar_height)
|
||||
glVertex2f(x, y + bar_height)
|
||||
glEnd()
|
||||
|
||||
# Power fill with gradient
|
||||
fill = self.power * bar_width
|
||||
if self.power < 0.3:
|
||||
glColor3f(0.0, 1.0, 0.0) # Green
|
||||
elif self.power < 0.7:
|
||||
glColor3f(1.0, 1.0, 0.0) # Yellow
|
||||
else:
|
||||
glColor3f(1.0, 0.0, 0.0) # Red
|
||||
|
||||
glBegin(GL_QUADS)
|
||||
glVertex2f(x, y)
|
||||
glVertex2f(x + fill, y)
|
||||
glVertex2f(x + fill, y + bar_height)
|
||||
glVertex2f(x, y + bar_height)
|
||||
glEnd()
|
||||
|
||||
# Border
|
||||
glColor3f(1.0, 1.0, 1.0)
|
||||
glLineWidth(2.0)
|
||||
glBegin(GL_LINE_LOOP)
|
||||
glVertex2f(x, y)
|
||||
glVertex2f(x + bar_width, y)
|
||||
glVertex2f(x + bar_width, y + bar_height)
|
||||
glVertex2f(x, y + bar_height)
|
||||
glEnd()
|
||||
|
||||
# Power label
|
||||
power_text = font.render("Power", True, (255, 255, 255))
|
||||
text_surface = pygame.image.tostring(power_text, 'RGBA', True)
|
||||
glRasterPos2f(x, y - 25)
|
||||
glDrawPixels(power_text.get_width(), power_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
# Angle indicator
|
||||
angle_y = y - 50
|
||||
glColor3f(0.5, 0.5, 1.0)
|
||||
glLineWidth(3.0)
|
||||
glBegin(GL_LINES)
|
||||
glVertex2f(x + bar_width/2, angle_y)
|
||||
glVertex2f(x + bar_width/2 + self.angle * 2, angle_y)
|
||||
glEnd()
|
||||
|
||||
# Angle label
|
||||
angle_text = font.render("Angle", True, (255, 255, 255))
|
||||
text_surface = pygame.image.tostring(angle_text, 'RGBA', True)
|
||||
glRasterPos2f(x, angle_y - 25)
|
||||
glDrawPixels(angle_text.get_width(), angle_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
# Instructions
|
||||
instructions = font.render("Use LEFT/RIGHT to aim, SPACE to charge power", True, (200, 200, 100))
|
||||
text_surface = pygame.image.tostring(instructions, 'RGBA', True)
|
||||
glRasterPos2f(self.width - instructions.get_width() - 20, 20)
|
||||
glDrawPixels(instructions.get_width(), instructions.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
# Display knocked pins for current roll
|
||||
if self.ball_done:
|
||||
knocked_text = font.render(f"Knocked down: {self.knocked_this_roll} pins", True, (255, 255, 255))
|
||||
text_surface = pygame.image.tostring(knocked_text, 'RGBA', True)
|
||||
glRasterPos2f(self.width/2 - knocked_text.get_width()/2, 50)
|
||||
glDrawPixels(knocked_text.get_width(), knocked_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
continue_text = font.render("Press SPACE to continue", True, (200, 200, 100))
|
||||
text_surface = pygame.image.tostring(continue_text, 'RGBA', True)
|
||||
glRasterPos2f(self.width/2 - continue_text.get_width()/2, 80)
|
||||
glDrawPixels(continue_text.get_width(), continue_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
|
||||
glPopMatrix()
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPopMatrix()
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glEnable(GL_LIGHTING)
|
||||
|
||||
def handle_input(self):
|
||||
keys = pygame.key.get_pressed()
|
||||
|
||||
# Camera controls
|
||||
if keys[K_a]:
|
||||
self.camera_angle -= 1
|
||||
if keys[K_d]:
|
||||
self.camera_angle += 1
|
||||
if keys[K_w] and self.camera_distance > 10:
|
||||
self.camera_distance -= 0.5
|
||||
if keys[K_s] and self.camera_distance < 30:
|
||||
self.camera_distance += 0.5
|
||||
|
||||
# Update camera position based on angle
|
||||
rad_angle = math.radians(self.camera_angle)
|
||||
self.camera_pos[0] = math.sin(rad_angle) * self.camera_distance
|
||||
self.camera_pos[2] = math.cos(rad_angle) * self.camera_distance + 5
|
||||
|
||||
if self.waiting_for_roll and not self.ball.launched:
|
||||
# Adjust angle
|
||||
if keys[K_LEFT]:
|
||||
self.angle -= 1
|
||||
self.angle = max(-30, self.angle)
|
||||
if keys[K_RIGHT]:
|
||||
self.angle += 1
|
||||
self.angle = min(30, self.angle)
|
||||
|
||||
# Charge power
|
||||
if keys[K_SPACE]:
|
||||
if not self.charging:
|
||||
self.charging = True
|
||||
self.power = 0
|
||||
else:
|
||||
self.power += 0.02
|
||||
if self.power > self.max_power:
|
||||
self.power = self.max_power
|
||||
else:
|
||||
if self.charging and self.power > 0.1: # Minimum power threshold
|
||||
self.ball.launch(self.power, self.angle)
|
||||
self.charging = False
|
||||
self.waiting_for_roll = False
|
||||
|
||||
# Continue to next roll
|
||||
if self.ball_done and keys[K_SPACE]:
|
||||
self.next_roll()
|
||||
|
||||
def next_roll(self):
|
||||
self.knocked_this_roll = self.count_knocked_pins()
|
||||
self.score += self.knocked_this_roll
|
||||
|
||||
# Game progression logic
|
||||
if self.roll == 1:
|
||||
if self.knocked_this_roll == 10: # Strike
|
||||
self.roll = 3 # Skip second roll
|
||||
else:
|
||||
self.roll = 2
|
||||
elif self.roll == 2:
|
||||
self.roll = 3
|
||||
|
||||
if self.roll == 3:
|
||||
self.frame += 1
|
||||
self.roll = 1
|
||||
self.reset_pins()
|
||||
|
||||
if self.frame > 10: # End of game
|
||||
self.game_over = True
|
||||
|
||||
self.reset_ball()
|
||||
|
||||
def run(self):
|
||||
clock = pygame.time.Clock()
|
||||
running = True
|
||||
|
||||
while running:
|
||||
dt = clock.tick(60) / 1000.0
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == QUIT:
|
||||
running = False
|
||||
elif event.type == KEYDOWN:
|
||||
if event.key == K_ESCAPE:
|
||||
running = False
|
||||
elif event.key == K_r:
|
||||
self.reset_ball()
|
||||
self.reset_pins()
|
||||
self.score = 0
|
||||
self.frame = 1
|
||||
self.roll = 1
|
||||
self.game_over = False
|
||||
|
||||
self.handle_input()
|
||||
|
||||
# Update game objects
|
||||
if not self.ball_done:
|
||||
self.ball_done = self.ball.update(dt, self.pins)
|
||||
|
||||
for pin in self.pins:
|
||||
pin.update(dt)
|
||||
|
||||
# Render
|
||||
glClearColor(*self.background_color, 1.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
|
||||
# Update camera target to follow ball
|
||||
if self.ball.launched and not self.ball_done:
|
||||
self.camera_target[0] = self.ball.pos[0] * 0.1
|
||||
self.camera_target[2] = self.ball.pos[2] * 0.1
|
||||
else:
|
||||
self.camera_target = [0, 1, 0]
|
||||
|
||||
gluLookAt(self.camera_pos[0], self.camera_pos[1], self.camera_pos[2],
|
||||
self.camera_target[0], self.camera_target[1], self.camera_target[2],
|
||||
0, 1, 0)
|
||||
|
||||
self.draw_lane()
|
||||
self.ball.draw()
|
||||
|
||||
for pin in self.pins:
|
||||
pin.draw()
|
||||
|
||||
self.draw_hud()
|
||||
|
||||
# Game over screen
|
||||
if self.game_over:
|
||||
glDisable(GL_LIGHTING)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
glOrtho(0, self.width, self.height, 0, -1, 1)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glPushMatrix()
|
||||
glLoadIdentity()
|
||||
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
|
||||
# Semi-transparent overlay
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glColor4f(0.0, 0.0, 0.0, 0.7)
|
||||
glBegin(GL_QUADS)
|
||||
glVertex2f(0, 0)
|
||||
glVertex2f(self.width, 0)
|
||||
glVertex2f(self.width, self.height)
|
||||
glVertex2f(0, self.height)
|
||||
glEnd()
|
||||
glDisable(GL_BLEND)
|
||||
|
||||
# Game over text
|
||||
font = pygame.font.SysFont('Arial', 48)
|
||||
game_over_text = font.render("GAME OVER", True, (255, 255, 255))
|
||||
text_surface = pygame.image.tostring(game_over_text, 'RGBA', True)
|
||||
glRasterPos2f(self.width/2 - game_over_text.get_width()/2, self.height/2 - 50)
|
||||
glDrawPixels(game_over_text.get_width(), game_over_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
score_text = font.render(f"Final Score: {self.score}", True, (255, 255, 255))
|
||||
text_surface = pygame.image.tostring(score_text, 'RGBA', True)
|
||||
glRasterPos2f(self.width/2 - score_text.get_width()/2, self.height/2 + 20)
|
||||
glDrawPixels(score_text.get_width(), score_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
restart_text = pygame.font.SysFont('Arial', 24).render("Press R to restart", True, (200, 200, 100))
|
||||
text_surface = pygame.image.tostring(restart_text, 'RGBA', True)
|
||||
glRasterPos2f(self.width/2 - restart_text.get_width()/2, self.height/2 + 80)
|
||||
glDrawPixels(restart_text.get_width(), restart_text.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, text_surface)
|
||||
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glPopMatrix()
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glPopMatrix()
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glEnable(GL_LIGHTING)
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
pygame.quit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
game = BowlingGame()
|
||||
game.run()
|
||||
275
simulations/balls/main.py
Normal file
275
simulations/balls/main.py
Normal file
@@ -0,0 +1,275 @@
|
||||
import pygame
|
||||
import math
|
||||
import random
|
||||
|
||||
# Initialize Pygame
|
||||
pygame.init()
|
||||
|
||||
# Screen setup
|
||||
WIDTH, HEIGHT = 800, 600
|
||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||
pygame.display.set_caption("Enhanced Physics Bouncing Balls")
|
||||
|
||||
# Colors
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
RED = (220, 50, 50)
|
||||
GREEN = (50, 220, 50)
|
||||
CYAN = (50, 220, 220)
|
||||
YELLOW = (220, 220, 50)
|
||||
|
||||
# Clock
|
||||
clock = pygame.time.Clock()
|
||||
FPS = 60
|
||||
|
||||
# Global physics
|
||||
gravity = 0.5
|
||||
elasticity = 0.85
|
||||
air_resistance = 0.99 # 1.0 = no resistance, lower = more resistance
|
||||
friction = 0.98 # Ground friction when rolling
|
||||
|
||||
# Ball class
|
||||
class Ball:
|
||||
def __init__(self, x, y):
|
||||
self.radius = 20
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.mass = self.radius / 10.0 # Mass proportional to radius
|
||||
# Give random initial velocity in both x and y
|
||||
self.vx = random.uniform(-5, 5)
|
||||
self.vy = random.uniform(-5, 0)
|
||||
self.tracer_length = 50
|
||||
self.tracers = []
|
||||
self.dragging = False
|
||||
self.drag_start_pos = None
|
||||
self.drag_last_pos = (x, y)
|
||||
self.drag_history = []
|
||||
|
||||
def update(self):
|
||||
if not self.dragging:
|
||||
# Apply gravity
|
||||
self.vy += gravity
|
||||
|
||||
# Apply air resistance (quadratic drag)
|
||||
speed = math.hypot(self.vx, self.vy)
|
||||
if speed > 0:
|
||||
drag_force = (1 - air_resistance) * speed
|
||||
drag_x = -drag_force * (self.vx / speed)
|
||||
drag_y = -drag_force * (self.vy / speed)
|
||||
self.vx += drag_x
|
||||
self.vy += drag_y
|
||||
|
||||
# Update position
|
||||
self.x += self.vx
|
||||
self.y += self.vy
|
||||
|
||||
# Bounce off floor with friction
|
||||
if self.y + self.radius > HEIGHT:
|
||||
self.y = HEIGHT - self.radius
|
||||
self.vy = -self.vy * elasticity
|
||||
self.vx *= friction # Apply friction when bouncing on ground
|
||||
|
||||
# Stop if moving very slowly
|
||||
if abs(self.vy) < 0.1:
|
||||
self.vy = 0
|
||||
if abs(self.vx) < 0.1:
|
||||
self.vx = 0
|
||||
|
||||
# Bounce off ceiling
|
||||
if self.y - self.radius < 0:
|
||||
self.y = self.radius
|
||||
self.vy = -self.vy * elasticity
|
||||
|
||||
# Bounce off right wall
|
||||
if self.x + self.radius > WIDTH:
|
||||
self.x = WIDTH - self.radius
|
||||
self.vx = -self.vx * elasticity
|
||||
|
||||
# Bounce off left wall
|
||||
if self.x - self.radius < 0:
|
||||
self.x = self.radius
|
||||
self.vx = -self.vx * elasticity
|
||||
|
||||
# Update tracers
|
||||
self.tracers.append((int(self.x), int(self.y)))
|
||||
if len(self.tracers) > self.tracer_length:
|
||||
self.tracers.pop(0)
|
||||
|
||||
def start_drag(self, mx, my):
|
||||
self.dragging = True
|
||||
self.drag_start_pos = (mx, my)
|
||||
self.drag_last_pos = (mx, my)
|
||||
self.drag_history = [(mx, my)]
|
||||
self.vx = 0
|
||||
self.vy = 0
|
||||
|
||||
def update_drag(self, mx, my):
|
||||
if self.dragging:
|
||||
self.x, self.y = mx, my
|
||||
self.drag_history.append((mx, my))
|
||||
if len(self.drag_history) > 5: # Keep last 5 positions for velocity calculation
|
||||
self.drag_history.pop(0)
|
||||
self.drag_last_pos = (mx, my)
|
||||
|
||||
def end_drag(self):
|
||||
if self.dragging and len(self.drag_history) >= 2:
|
||||
# Calculate velocity from drag motion (throw mechanics)
|
||||
dx = self.drag_history[-1][0] - self.drag_history[0][0]
|
||||
dy = self.drag_history[-1][1] - self.drag_history[0][1]
|
||||
frames = len(self.drag_history)
|
||||
|
||||
# Set velocity based on drag motion
|
||||
self.vx = dx / frames * 2
|
||||
self.vy = dy / frames * 2
|
||||
|
||||
self.dragging = False
|
||||
self.drag_start_pos = None
|
||||
self.drag_history = []
|
||||
|
||||
def draw(self, surface):
|
||||
# Draw tracers with fade effect
|
||||
for i, pos in enumerate(self.tracers):
|
||||
alpha = int(255 * (i / len(self.tracers)))
|
||||
size = int(3 + 2 * (i / len(self.tracers)))
|
||||
color = (50, 220, 220)
|
||||
pygame.draw.circle(surface, color, pos, size)
|
||||
|
||||
# Draw ball
|
||||
pygame.draw.circle(surface, RED, (int(self.x), int(self.y)), self.radius)
|
||||
|
||||
# Draw velocity vector (scaled for visibility)
|
||||
if not self.dragging:
|
||||
vel_scale = 3
|
||||
pygame.draw.line(surface, GREEN,
|
||||
(int(self.x), int(self.y)),
|
||||
(int(self.x + self.vx * vel_scale),
|
||||
int(self.y + self.vy * vel_scale)), 3)
|
||||
|
||||
# Draw drag trajectory preview
|
||||
if self.dragging and self.drag_start_pos:
|
||||
pygame.draw.line(surface, YELLOW,
|
||||
self.drag_start_pos,
|
||||
(int(self.x), int(self.y)), 2)
|
||||
# Draw arrow at the end
|
||||
pygame.draw.circle(surface, YELLOW, (int(self.x), int(self.y)), 5)
|
||||
|
||||
# Ball-to-ball collision detection and response
|
||||
def handle_ball_collisions(balls):
|
||||
for i, ball1 in enumerate(balls):
|
||||
for ball2 in balls[i+1:]:
|
||||
dx = ball2.x - ball1.x
|
||||
dy = ball2.y - ball1.y
|
||||
distance = math.hypot(dx, dy)
|
||||
|
||||
# Check if balls are colliding
|
||||
if distance < ball1.radius + ball2.radius and distance > 0:
|
||||
# Normalize collision vector
|
||||
nx = dx / distance
|
||||
ny = dy / distance
|
||||
|
||||
# Separate balls
|
||||
overlap = (ball1.radius + ball2.radius) - distance
|
||||
ball1.x -= nx * overlap * 0.5
|
||||
ball1.y -= ny * overlap * 0.5
|
||||
ball2.x += nx * overlap * 0.5
|
||||
ball2.y += ny * overlap * 0.5
|
||||
|
||||
# Calculate relative velocity
|
||||
dvx = ball2.vx - ball1.vx
|
||||
dvy = ball2.vy - ball1.vy
|
||||
|
||||
# Calculate relative velocity in collision normal direction
|
||||
dot_product = dvx * nx + dvy * ny
|
||||
|
||||
# Only resolve if balls are moving toward each other
|
||||
if dot_product < 0:
|
||||
# Calculate impulse (conservation of momentum with equal mass)
|
||||
impulse = 2 * dot_product / (ball1.mass + ball2.mass)
|
||||
|
||||
# Apply impulse to both balls
|
||||
ball1.vx += impulse * ball2.mass * nx * elasticity
|
||||
ball1.vy += impulse * ball2.mass * ny * elasticity
|
||||
ball2.vx -= impulse * ball1.mass * nx * elasticity
|
||||
ball2.vy -= impulse * ball1.mass * ny * elasticity
|
||||
|
||||
# List of balls
|
||||
balls = [Ball(WIDTH//2, HEIGHT//2)]
|
||||
|
||||
running = True
|
||||
while running:
|
||||
mx, my = pygame.mouse.get_pos()
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
|
||||
# Drag start
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
for ball in balls:
|
||||
if math.hypot(mx - ball.x, my - ball.y) < ball.radius + 10:
|
||||
ball.start_drag(mx, my)
|
||||
break
|
||||
|
||||
# Drag end
|
||||
elif event.type == pygame.MOUSEBUTTONUP:
|
||||
for ball in balls:
|
||||
if ball.dragging:
|
||||
ball.end_drag()
|
||||
|
||||
# Spawn new ball
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_SPACE:
|
||||
balls.append(Ball(random.randint(50, WIDTH-50),
|
||||
random.randint(50, HEIGHT-50)))
|
||||
elif event.key == pygame.K_c:
|
||||
balls.clear()
|
||||
balls.append(Ball(WIDTH//2, HEIGHT//2))
|
||||
|
||||
# Keyboard control for physics
|
||||
keys = pygame.key.get_pressed()
|
||||
if keys[pygame.K_UP]:
|
||||
gravity = min(2.0, gravity + 0.01)
|
||||
if keys[pygame.K_DOWN]:
|
||||
gravity = max(0, gravity - 0.01)
|
||||
if keys[pygame.K_RIGHT]:
|
||||
elasticity = min(1.0, elasticity + 0.01)
|
||||
if keys[pygame.K_LEFT]:
|
||||
elasticity = max(0, elasticity - 0.01)
|
||||
if keys[pygame.K_w]:
|
||||
air_resistance = min(1.0, air_resistance + 0.001)
|
||||
if keys[pygame.K_s]:
|
||||
air_resistance = max(0.9, air_resistance - 0.001)
|
||||
|
||||
# Update dragging balls
|
||||
for ball in balls:
|
||||
if ball.dragging:
|
||||
ball.update_drag(mx, my)
|
||||
|
||||
# Update balls
|
||||
for ball in balls:
|
||||
ball.update()
|
||||
|
||||
# Handle collisions between balls
|
||||
handle_ball_collisions(balls)
|
||||
|
||||
# Drawing
|
||||
screen.fill(BLACK)
|
||||
for ball in balls:
|
||||
ball.draw(screen)
|
||||
|
||||
# Physics info
|
||||
font = pygame.font.SysFont(None, 22)
|
||||
info = [
|
||||
f"Gravity: {gravity:.2f} (↑↓) Elasticity: {elasticity:.2f} (←→)",
|
||||
f"Air Resistance: {air_resistance:.3f} (WS) Balls: {len(balls)}",
|
||||
f"SPACE: Add ball C: Clear Drag to throw!"
|
||||
]
|
||||
|
||||
for i, text in enumerate(info):
|
||||
surface = font.render(text, True, WHITE)
|
||||
screen.blit(surface, (10, 10 + i * 25))
|
||||
|
||||
pygame.display.flip()
|
||||
clock.tick(FPS)
|
||||
|
||||
pygame.quit()
|
||||
57
simulations/donut.c/donut.cpp
Normal file
57
simulations/donut.c/donut.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <windows.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
int main() {
|
||||
float A = 0, B = 0;
|
||||
float i, j;
|
||||
int k;
|
||||
float z[1760];
|
||||
char b[1760];
|
||||
|
||||
// Clear screen
|
||||
std::printf("\x1b[2J");
|
||||
|
||||
for (;;) {
|
||||
std::memset(b, 32, 1760); // fill with spaces
|
||||
std::memset(z, 0, 1760 * sizeof(float));
|
||||
|
||||
for (j = 0; j < 6.28; j += 0.07) {
|
||||
for (i = 0; i < 6.28; i += 0.02) {
|
||||
float c = std::sin(i);
|
||||
float d = std::cos(j);
|
||||
float e = std::sin(A);
|
||||
float f = std::sin(j);
|
||||
float g = std::cos(A);
|
||||
float h = d + 2;
|
||||
float D = 1 / (c * h * e + f * g + 5);
|
||||
float l = std::cos(i);
|
||||
float m = std::cos(B);
|
||||
float n = std::sin(B);
|
||||
float t = c * h * g - f * e;
|
||||
int x = int(40 + 30 * D * (l * h * m - t * n));
|
||||
int y = int(12 + 15 * D * (l * h * n + t * m));
|
||||
int o = x + 80 * y;
|
||||
int N = int(8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n));
|
||||
|
||||
if (y >= 0 && y < 22 && x >= 0 && x < 80 && D > z[o]) {
|
||||
z[o] = D;
|
||||
b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::printf("\x1b[H");
|
||||
for (k = 0; k < 1761; k++) {
|
||||
putchar(k % 80 ? b[k] : 10);
|
||||
}
|
||||
|
||||
A += 0.04f;
|
||||
B += 0.02f;
|
||||
|
||||
Sleep(10); // milliseconds
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
55
simulations/donut.c/main.py
Normal file
55
simulations/donut.c/main.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import math
|
||||
def render_frame(A, B):
|
||||
cosA = math.cos(A)
|
||||
sinA = math.sin(A)
|
||||
cosB = math.cos(B)
|
||||
sinB = math.sin(B)
|
||||
char_output = []
|
||||
zbuffer = []
|
||||
for i in range(screen_height + 1):
|
||||
char_output.append([' '] * (screen_width + 0))
|
||||
zbuffer.append([0] * (screen_width + 0))
|
||||
theta = 0
|
||||
while (theta < 2* math.pi):
|
||||
theta += theta_spacing
|
||||
costheta = math.cos(theta)
|
||||
sintheta = math.sin(theta)
|
||||
phi = 0
|
||||
while (phi < 2*math.pi):
|
||||
phi += phi_spacing
|
||||
cosphi = math.cos(phi)
|
||||
sinphi = math.sin(phi)
|
||||
circlex = R2 + R1*costheta
|
||||
circley = R1*sintheta
|
||||
x = circlex*(cosB*cosphi + sinA*sinB*sinphi) - circley*cosA*sinB
|
||||
y = circlex*(sinB*cosphi - sinA*cosB*sinphi) + circley*cosA*cosB
|
||||
z = K2 + cosA*circlex*sinphi + circley*sinA
|
||||
ooz = 1/z
|
||||
xp = int(screen_width/2 + K1*ooz*x)
|
||||
yp = int(screen_height/2 - K1*ooz*y)
|
||||
L = cosphi*costheta*sinB - cosA*costheta*sinphi - sinA*sintheta + cosB*(cosA*sintheta - costheta*sinA*sinphi)
|
||||
if L > 0:
|
||||
if ooz > zbuffer[xp][yp]:
|
||||
zbuffer[xp][yp] = ooz
|
||||
luminance_index = L*8
|
||||
char_output[xp][yp] = '.,-~:;=!*#$@'[int(luminance_index)]
|
||||
print('\x1b[H')
|
||||
for i in range(screen_height):
|
||||
for j in range(screen_width):
|
||||
print(char_output[i][j], end='')
|
||||
print()
|
||||
theta_spacing = 0.07
|
||||
phi_spacing = 0.02
|
||||
R1 = 1
|
||||
R2 = 2
|
||||
K2 = 5
|
||||
screen_width = 35
|
||||
screen_height = 35
|
||||
K1 = screen_width*K2*3/(8*(R1+R2))
|
||||
print('\x1b[2J')
|
||||
A = 1.0
|
||||
B = 1.0
|
||||
for i in range(250):
|
||||
render_frame(A, B)
|
||||
A += 0.08
|
||||
B += 0.03
|
||||
113
simulations/donut.c/tktcl.py
Normal file
113
simulations/donut.c/tktcl.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import tkinter as tk
|
||||
import math
|
||||
|
||||
# Torus parameters
|
||||
theta_spacing = 0.07
|
||||
phi_spacing = 0.02
|
||||
R1 = 1
|
||||
R2 = 2
|
||||
K2 = 5
|
||||
|
||||
chars = ".,-~:;=!*#$@" # luminance chars
|
||||
|
||||
# Base character grid (like "screen resolution")
|
||||
screen_width = 80
|
||||
screen_height = 24
|
||||
|
||||
# Tkinter setup
|
||||
root = tk.Tk()
|
||||
root.title("Spinning Torus Demo")
|
||||
|
||||
# Get window size and calculate scaling
|
||||
window_width = 800
|
||||
window_height = 400
|
||||
root.geometry(f"{window_width}x{window_height}")
|
||||
|
||||
# Calculate character cell size to fit screen dimensions
|
||||
cell_width = window_width // screen_width
|
||||
cell_height = window_height // screen_height
|
||||
font_size = min(cell_width, cell_height)
|
||||
font = ("Courier", font_size)
|
||||
|
||||
canvas = tk.Canvas(root, width=window_width, height=window_height, bg="black")
|
||||
canvas.pack()
|
||||
|
||||
# Pre-create text items for the character grid
|
||||
text_items = []
|
||||
for y in range(screen_height):
|
||||
row = []
|
||||
for x in range(screen_width):
|
||||
item = canvas.create_text(
|
||||
x*cell_width, y*cell_height,
|
||||
text=' ',
|
||||
anchor='nw',
|
||||
fill='white',
|
||||
font=font
|
||||
)
|
||||
row.append(item)
|
||||
text_items.append(row)
|
||||
|
||||
# Calculate K1 based on screen width
|
||||
K1 = screen_width * K2 * 3 / (8 * (R1 + R2))
|
||||
|
||||
def render_frame(A, B):
|
||||
cosA = math.cos(A)
|
||||
sinA = math.sin(A)
|
||||
cosB = math.cos(B)
|
||||
sinB = math.sin(B)
|
||||
|
||||
output = [[' ' for _ in range(screen_height)] for _ in range(screen_width)]
|
||||
zbuffer = [[0 for _ in range(screen_height)] for _ in range(screen_width)]
|
||||
|
||||
theta = 0
|
||||
while theta < 2 * math.pi:
|
||||
costheta = math.cos(theta)
|
||||
sintheta = math.sin(theta)
|
||||
|
||||
phi = 0
|
||||
while phi < 2 * math.pi:
|
||||
cosphi = math.cos(phi)
|
||||
sinphi = math.sin(phi)
|
||||
|
||||
circlex = R2 + R1 * costheta
|
||||
circley = R1 * sintheta
|
||||
|
||||
x = circlex * (cosB * cosphi + sinA * sinB * sinphi) - circley * cosA * sinB
|
||||
y = circlex * (sinB * cosphi - sinA * cosB * sinphi) + circley * cosA * cosB
|
||||
z = K2 + cosA * circlex * sinphi + circley * sinA
|
||||
ooz = 1 / z
|
||||
|
||||
xp = int(screen_width / 2 + K1 * ooz * x)
|
||||
yp = int(screen_height / 2 - K1 * ooz * y)
|
||||
|
||||
L = cosphi * costheta * sinB - cosA * costheta * sinphi - sinA * sintheta + cosB * (cosA * sintheta - costheta * sinA * sinphi)
|
||||
|
||||
if L > 0:
|
||||
if 0 <= xp < screen_width and 0 <= yp < screen_height:
|
||||
if ooz > zbuffer[xp][yp]:
|
||||
zbuffer[xp][yp] = ooz
|
||||
luminance_index = int(L * 8)
|
||||
if luminance_index >= len(chars):
|
||||
luminance_index = len(chars) - 1
|
||||
output[xp][yp] = chars[luminance_index]
|
||||
|
||||
phi += phi_spacing
|
||||
theta += theta_spacing
|
||||
|
||||
# Update Tkinter canvas
|
||||
for y in range(screen_height):
|
||||
for x in range(screen_width):
|
||||
canvas.itemconfigure(text_items[y][x], text=output[x][y])
|
||||
|
||||
# Animation loop
|
||||
A = 0
|
||||
B = 0
|
||||
def animate():
|
||||
global A, B
|
||||
render_frame(A, B)
|
||||
A += 0.07
|
||||
B += 0.03
|
||||
root.after(30, animate)
|
||||
|
||||
animate()
|
||||
root.mainloop()
|
||||
21
simulations/mandelbrotset/CppProperties.json
Normal file
21
simulations/mandelbrotset/CppProperties.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"inheritEnvironments": [
|
||||
"msvc_x86"
|
||||
],
|
||||
"name": "x86-Debug",
|
||||
"includePath": [
|
||||
"${env.INCLUDE}",
|
||||
"${workspaceRoot}\\**"
|
||||
],
|
||||
"defines": [
|
||||
"WIN32",
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE"
|
||||
],
|
||||
"intelliSenseMode": "windows-msvc-x86"
|
||||
}
|
||||
]
|
||||
}
|
||||
22
simulations/mandelbrotset/cpp/main.cpp
Normal file
22
simulations/mandelbrotset/cpp/main.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <windows.h>
|
||||
#include "mandelbrot_app.h"
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
|
||||
// Check for DirectX 11 support
|
||||
if (FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0,
|
||||
D3D11_SDK_VERSION, nullptr, nullptr, nullptr))) {
|
||||
MessageBox(nullptr, L"DirectX 11 is not available on this system.", L"Error", MB_OK | MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
MandelbrotApp app(hInstance);
|
||||
app.Run();
|
||||
}
|
||||
catch (...) {
|
||||
MessageBox(nullptr, L"An unexpected error occurred.", L"Error", MB_OK | MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
927
simulations/mandelbrotset/cpp/mandelbrot_app.cpp
Normal file
927
simulations/mandelbrotset/cpp/mandelbrot_app.cpp
Normal file
@@ -0,0 +1,927 @@
|
||||
#include <windows.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <wrl/client.h>
|
||||
#include <algorithm>
|
||||
#include <tchar.h>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include "mandelbrot_app.h"
|
||||
|
||||
#ifndef GET_X_LPARAM
|
||||
#define GET_X_LPARAM(lParam) ((int)(short)LOWORD(lParam))
|
||||
#endif
|
||||
|
||||
#ifndef GET_Y_LPARAM
|
||||
#define GET_Y_LPARAM(lParam) ((int)(short)HIWORD(lParam))
|
||||
#endif
|
||||
|
||||
const char* computeShaderSource = R"(
|
||||
cbuffer CB : register(b0) {
|
||||
float xmin, xmax, ymin, ymax;
|
||||
int width, height, maxIter;
|
||||
float time;
|
||||
};
|
||||
|
||||
RWTexture2D<float4> Output : register(u0);
|
||||
|
||||
float3 palette(float t, int scheme) {
|
||||
if (scheme == 0) {
|
||||
float3 a = float3(0.5, 0.5, 0.5);
|
||||
float3 b = float3(0.5, 0.5, 0.5);
|
||||
float3 c = float3(1.0, 1.0, 1.0);
|
||||
float3 d = float3(0.0, 0.33, 0.67);
|
||||
return a + b * cos(6.28318 * (c * t + d));
|
||||
} else if (scheme == 1) {
|
||||
float3 a = float3(0.5, 0.5, 0.0);
|
||||
float3 b = float3(0.5, 0.5, 0.0);
|
||||
float3 c = float3(1.0, 0.7, 0.4);
|
||||
float3 d = float3(0.0, 0.15, 0.20);
|
||||
return a + b * cos(6.28318 * (c * t + d));
|
||||
} else if (scheme == 2) {
|
||||
float3 a = float3(0.2, 0.5, 0.8);
|
||||
float3 b = float3(0.2, 0.4, 0.2);
|
||||
float3 c = float3(2.0, 1.0, 1.0);
|
||||
float3 d = float3(0.0, 0.25, 0.25);
|
||||
return a + b * cos(6.28318 * (c * t + d));
|
||||
} else if (scheme == 3) {
|
||||
float3 a = float3(0.5, 0.2, 0.8);
|
||||
float3 b = float3(0.5, 0.5, 0.5);
|
||||
float3 c = float3(1.0, 1.0, 0.5);
|
||||
float3 d = float3(0.8, 0.9, 0.3);
|
||||
return a + b * cos(6.28318 * (c * t + d));
|
||||
} else {
|
||||
float v = 0.5 + 0.5 * cos(6.28318 * t);
|
||||
return float3(v, v, v);
|
||||
}
|
||||
}
|
||||
|
||||
[numthreads(8, 8, 1)]
|
||||
void main(uint3 DTid : SV_DispatchThreadID) {
|
||||
int x = DTid.x;
|
||||
int y = DTid.y;
|
||||
|
||||
if (x >= width || y >= height) return;
|
||||
|
||||
float cx = xmin + (xmax - xmin) * x / (float)width;
|
||||
float cy = ymin + (ymax - ymin) * y / (float)height;
|
||||
|
||||
float zx = 0.0;
|
||||
float zy = 0.0;
|
||||
float zx2 = 0.0;
|
||||
float zy2 = 0.0;
|
||||
int iter = 0;
|
||||
|
||||
while (iter < maxIter && (zx2 + zy2) < 4.0) {
|
||||
zy = 2.0 * zx * zy + cy;
|
||||
zx = zx2 - zy2 + cx;
|
||||
zx2 = zx * zx;
|
||||
zy2 = zy * zy;
|
||||
iter++;
|
||||
}
|
||||
|
||||
float4 color;
|
||||
if (iter == maxIter) {
|
||||
color = float4(0.0, 0.0, 0.0, 1.0);
|
||||
} else {
|
||||
float log_zn = log(zx2 + zy2) * 0.5;
|
||||
float nu = log2(log_zn);
|
||||
float smooth_iter = iter + 1.0 - nu;
|
||||
|
||||
int scheme = maxIter >> 16;
|
||||
int actualMaxIter = maxIter & 0xFFFF;
|
||||
|
||||
float t = smooth_iter / 50.0 + time * 0.02;
|
||||
float3 rgb = palette(t, scheme);
|
||||
|
||||
float brightness = 0.5 + 0.5 * sin(smooth_iter * 0.1);
|
||||
rgb *= brightness;
|
||||
|
||||
color = float4(rgb, 1.0);
|
||||
}
|
||||
|
||||
Output[uint2(x, y)] = color;
|
||||
}
|
||||
)";
|
||||
|
||||
const char* vertexShaderSource = R"(
|
||||
struct VS_OUTPUT {
|
||||
float4 pos : SV_POSITION;
|
||||
float2 tex : TEXCOORD0;
|
||||
};
|
||||
|
||||
VS_OUTPUT main(uint id : SV_VertexID) {
|
||||
VS_OUTPUT output;
|
||||
output.tex = float2((id << 1) & 2, id & 2);
|
||||
output.pos = float4(output.tex * float2(2, -2) + float2(-1, 1), 0, 1);
|
||||
return output;
|
||||
}
|
||||
)";
|
||||
|
||||
const char* pixelShaderSource = R"(
|
||||
Texture2D tex : register(t0);
|
||||
SamplerState samp : register(s0);
|
||||
|
||||
float4 main(float4 pos : SV_POSITION, float2 texCoord : TEXCOORD0) : SV_TARGET {
|
||||
return tex.Sample(samp, texCoord);
|
||||
}
|
||||
)";
|
||||
|
||||
MandelbrotApp::MandelbrotApp(HINSTANCE hInstance) : hInstance_(hInstance) {
|
||||
QueryPerformanceFrequency(&perfFreq_);
|
||||
QueryPerformanceCounter(&lastFrameTime_);
|
||||
|
||||
CreateMainWindow();
|
||||
if (hwnd_) {
|
||||
InitD3D();
|
||||
CreateComputeShader();
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
MandelbrotApp::~MandelbrotApp() {
|
||||
if (context_) context_->ClearState();
|
||||
}
|
||||
|
||||
void MandelbrotApp::CreateMainWindow() {
|
||||
WNDCLASSEX wc = {};
|
||||
wc.cbSize = sizeof(WNDCLASSEX);
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.hInstance = hInstance_;
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wc.lpszClassName = _T("MandelbrotViewerGPU");
|
||||
|
||||
if (!RegisterClassEx(&wc)) {
|
||||
MessageBox(nullptr, L"Failed to register window class", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
RECT rc = { 0, 0, 1280, 720 };
|
||||
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
|
||||
hwnd_ = CreateWindowEx(
|
||||
0,
|
||||
_T("MandelbrotViewerGPU"),
|
||||
_T("GPU Mandelbrot Viewer"),
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
rc.right - rc.left, rc.bottom - rc.top,
|
||||
nullptr, nullptr, hInstance_, this
|
||||
);
|
||||
|
||||
if (!hwnd_) {
|
||||
MessageBox(nullptr, L"Failed to create window", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
ShowWindow(hwnd_, SW_SHOWDEFAULT);
|
||||
UpdateWindow(hwnd_);
|
||||
}
|
||||
|
||||
void MandelbrotApp::InitD3D() {
|
||||
RECT rc;
|
||||
GetClientRect(hwnd_, &rc);
|
||||
|
||||
imageWidth_ = rc.right - rc.left;
|
||||
imageHeight_ = rc.bottom - rc.top;
|
||||
if (imageWidth_ < 1) imageWidth_ = 1;
|
||||
if (imageHeight_ < 1) imageHeight_ = 1;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC scd = {};
|
||||
scd.BufferCount = 2;
|
||||
scd.BufferDesc.Width = imageWidth_;
|
||||
scd.BufferDesc.Height = imageHeight_;
|
||||
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
scd.BufferDesc.RefreshRate.Numerator = 60;
|
||||
scd.BufferDesc.RefreshRate.Denominator = 1;
|
||||
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
scd.OutputWindow = hwnd_;
|
||||
scd.SampleDesc.Count = 1;
|
||||
scd.SampleDesc.Quality = 0;
|
||||
scd.Windowed = TRUE;
|
||||
scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||
|
||||
D3D_FEATURE_LEVEL featureLevels[] = {
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_1,
|
||||
D3D_FEATURE_LEVEL_10_0
|
||||
};
|
||||
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
|
||||
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
UINT flags = 0;
|
||||
|
||||
HRESULT hr = D3D11CreateDeviceAndSwapChain(
|
||||
nullptr,
|
||||
D3D_DRIVER_TYPE_HARDWARE,
|
||||
nullptr,
|
||||
flags,
|
||||
featureLevels,
|
||||
numFeatureLevels,
|
||||
D3D11_SDK_VERSION,
|
||||
&scd,
|
||||
&swapChain_,
|
||||
&device_,
|
||||
&featureLevel,
|
||||
&context_
|
||||
);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create D3D11 device and swap chain", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
ComPtr<ID3D11Texture2D> backBuffer;
|
||||
hr = swapChain_->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to get swap chain back buffer", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
hr = device_->CreateRenderTargetView(backBuffer.Get(), nullptr, &renderTargetView_);
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create render target view", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
D3D11_SAMPLER_DESC sampDesc = {};
|
||||
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
||||
sampDesc.MinLOD = 0;
|
||||
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
|
||||
|
||||
hr = device_->CreateSamplerState(&sampDesc, &samplerState_);
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create sampler state", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the viewport
|
||||
D3D11_VIEWPORT vp;
|
||||
vp.Width = (float)imageWidth_;
|
||||
vp.Height = (float)imageHeight_;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = 0;
|
||||
vp.TopLeftY = 0;
|
||||
context_->RSSetViewports(1, &vp);
|
||||
|
||||
// Set render target
|
||||
context_->OMSetRenderTargets(1, renderTargetView_.GetAddressOf(), nullptr);
|
||||
|
||||
ResizeBuffers(imageWidth_, imageHeight_);
|
||||
}
|
||||
|
||||
void MandelbrotApp::CreateComputeShader() {
|
||||
if (!device_) return;
|
||||
|
||||
ComPtr<ID3DBlob> csBlob, errBlob;
|
||||
|
||||
HRESULT hr = D3DCompile(
|
||||
computeShaderSource,
|
||||
strlen(computeShaderSource),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"main",
|
||||
"cs_5_0",
|
||||
D3DCOMPILE_ENABLE_STRICTNESS,
|
||||
0,
|
||||
&csBlob,
|
||||
&errBlob
|
||||
);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (errBlob) {
|
||||
OutputDebugStringA((char*)errBlob->GetBufferPointer());
|
||||
}
|
||||
MessageBox(nullptr, L"Failed to compile compute shader", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
hr = device_->CreateComputeShader(csBlob->GetBufferPointer(), csBlob->GetBufferSize(), nullptr, &computeShader_);
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create compute shader", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vertex shader
|
||||
ComPtr<ID3DBlob> vsBlob;
|
||||
hr = D3DCompile(
|
||||
vertexShaderSource,
|
||||
strlen(vertexShaderSource),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"main",
|
||||
"vs_5_0",
|
||||
D3DCOMPILE_ENABLE_STRICTNESS,
|
||||
0,
|
||||
&vsBlob,
|
||||
&errBlob
|
||||
);
|
||||
if (SUCCEEDED(hr)) {
|
||||
device_->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, &vertexShader_);
|
||||
}
|
||||
else {
|
||||
MessageBox(nullptr, L"Failed to compile vertex shader", L"Error", MB_OK);
|
||||
}
|
||||
|
||||
// Pixel shader
|
||||
ComPtr<ID3DBlob> psBlob;
|
||||
hr = D3DCompile(
|
||||
pixelShaderSource,
|
||||
strlen(pixelShaderSource),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"main",
|
||||
"ps_5_0",
|
||||
D3DCOMPILE_ENABLE_STRICTNESS,
|
||||
0,
|
||||
&psBlob,
|
||||
&errBlob
|
||||
);
|
||||
if (SUCCEEDED(hr)) {
|
||||
device_->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, &pixelShader_);
|
||||
}
|
||||
else {
|
||||
MessageBox(nullptr, L"Failed to compile pixel shader", L"Error", MB_OK);
|
||||
}
|
||||
|
||||
// Constant buffer - FIXED: Ensure proper alignment
|
||||
D3D11_BUFFER_DESC cbDesc = {};
|
||||
cbDesc.ByteWidth = (sizeof(ConstantBuffer) + 15) & ~15; // Align to 16 bytes
|
||||
cbDesc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
cbDesc.MiscFlags = 0;
|
||||
cbDesc.StructureByteStride = 0;
|
||||
|
||||
hr = device_->CreateBuffer(&cbDesc, nullptr, &constantBuffer_);
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create constant buffer", L"Error", MB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void MandelbrotApp::ResizeBuffers(int width, int height) {
|
||||
if (width <= 0 || height <= 0 || !device_) return;
|
||||
|
||||
outputTexture_.Reset();
|
||||
outputUAV_.Reset();
|
||||
outputSRV_.Reset();
|
||||
|
||||
// Create output texture for compute shader
|
||||
D3D11_TEXTURE2D_DESC texDesc = {};
|
||||
texDesc.Width = width;
|
||||
texDesc.Height = height;
|
||||
texDesc.MipLevels = 1;
|
||||
texDesc.ArraySize = 1;
|
||||
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
texDesc.SampleDesc.Count = 1;
|
||||
texDesc.SampleDesc.Quality = 0;
|
||||
texDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
texDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
|
||||
texDesc.CPUAccessFlags = 0;
|
||||
texDesc.MiscFlags = 0;
|
||||
|
||||
HRESULT hr = device_->CreateTexture2D(&texDesc, nullptr, &outputTexture_);
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create output texture", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create UAV for compute shader
|
||||
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
||||
uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
|
||||
uavDesc.Texture2D.MipSlice = 0;
|
||||
|
||||
hr = device_->CreateUnorderedAccessView(outputTexture_.Get(), &uavDesc, &outputUAV_);
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create UAV", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create SRV for pixel shader
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
|
||||
hr = device_->CreateShaderResourceView(outputTexture_.Get(), &srvDesc, &outputSRV_);
|
||||
if (FAILED(hr)) {
|
||||
MessageBox(nullptr, L"Failed to create SRV", L"Error", MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
imageWidth_ = width;
|
||||
imageHeight_ = height;
|
||||
}
|
||||
|
||||
void MandelbrotApp::UpdateTitle() {
|
||||
double zoom = 4.0 / (xmax_ - xmin_);
|
||||
std::wostringstream title;
|
||||
title << L"Mandelbrot Viewer | Zoom: " << std::fixed << std::setprecision(2) << zoom
|
||||
<< L"x | Iter: " << maxIter_
|
||||
<< L" | Color: " << colorScheme_ + 1
|
||||
<< L" | Anim: " << (animationEnabled_ ? L"ON" : L"OFF")
|
||||
<< L" | [H for Help]";
|
||||
SetWindowText(hwnd_, title.str().c_str());
|
||||
}
|
||||
|
||||
void MandelbrotApp::SaveBookmark() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (!bookmarks_[i].saved || i == 0) {
|
||||
std::lock_guard<std::mutex> lock(viewMutex_);
|
||||
bookmarks_[i].xmin = xmin_;
|
||||
bookmarks_[i].xmax = xmax_;
|
||||
bookmarks_[i].ymin = ymin_;
|
||||
bookmarks_[i].ymax = ymax_;
|
||||
bookmarks_[i].maxIter = maxIter_;
|
||||
bookmarks_[i].saved = true;
|
||||
|
||||
std::wostringstream msg;
|
||||
msg << L"Bookmark saved to slot " << i;
|
||||
MessageBox(hwnd_, msg.str().c_str(), L"Bookmark", MB_OK | MB_ICONINFORMATION);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MandelbrotApp::LoadBookmark(int slot) {
|
||||
if (slot >= 0 && slot < 10 && bookmarks_[slot].saved) {
|
||||
std::lock_guard<std::mutex> lock(viewMutex_);
|
||||
xmin_ = bookmarks_[slot].xmin;
|
||||
xmax_ = bookmarks_[slot].xmax;
|
||||
ymin_ = bookmarks_[slot].ymin;
|
||||
ymax_ = bookmarks_[slot].ymax;
|
||||
maxIter_ = bookmarks_[slot].maxIter;
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
void MandelbrotApp::ResetView() {
|
||||
std::lock_guard<std::mutex> lock(viewMutex_);
|
||||
xmin_ = -2.5; xmax_ = 1.5;
|
||||
ymin_ = -1.5; ymax_ = 1.5;
|
||||
maxIter_ = 256;
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void MandelbrotApp::ToggleAnimation() {
|
||||
animationEnabled_ = !animationEnabled_;
|
||||
if (!animationEnabled_) {
|
||||
animTime_ = 0.0f;
|
||||
}
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void MandelbrotApp::AdjustIterations(int delta) {
|
||||
std::lock_guard<std::mutex> lock(viewMutex_);
|
||||
maxIter_ = std::max(64, std::min(8192, maxIter_ + delta));
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void MandelbrotApp::CycleColorScheme() {
|
||||
colorScheme_ = (colorScheme_ + 1) % 5;
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void MandelbrotApp::Zoom(double factor, int centerX, int centerY) {
|
||||
std::lock_guard<std::mutex> lock(viewMutex_);
|
||||
|
||||
double centerReal = xmin_ + (xmax_ - xmin_) * centerX / imageWidth_;
|
||||
double centerImag = ymin_ + (ymax_ - ymin_) * centerY / imageHeight_;
|
||||
|
||||
double width = (xmax_ - xmin_) * factor;
|
||||
double height = (ymax_ - ymin_) * factor;
|
||||
|
||||
xmin_ = centerReal - width * 0.5;
|
||||
xmax_ = centerReal + width * 0.5;
|
||||
ymin_ = centerImag - height * 0.5;
|
||||
ymax_ = centerImag + height * 0.5;
|
||||
}
|
||||
|
||||
void MandelbrotApp::Pan(int dx, int dy) {
|
||||
std::lock_guard<std::mutex> lock(viewMutex_);
|
||||
|
||||
double deltaX = (xmax_ - xmin_) * dx / imageWidth_;
|
||||
double deltaY = (ymax_ - ymin_) * dy / imageHeight_;
|
||||
|
||||
xmin_ -= deltaX;
|
||||
xmax_ -= deltaX;
|
||||
ymin_ += deltaY;
|
||||
ymax_ += deltaY;
|
||||
}
|
||||
|
||||
void MandelbrotApp::RenderMandelbrot() {
|
||||
if (!device_ || !context_ || !swapChain_ || !computeShader_ ||
|
||||
!constantBuffer_ || !outputUAV_ || !renderTargetView_) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConstantBuffer cb;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(viewMutex_);
|
||||
|
||||
double zoom = 4.0 / (xmax_ - xmin_);
|
||||
int adaptiveMaxIter = maxIter_;
|
||||
if (zoom > 10) adaptiveMaxIter = std::max(maxIter_, 512);
|
||||
if (zoom > 100) adaptiveMaxIter = std::max(maxIter_, 1024);
|
||||
if (zoom > 1000) adaptiveMaxIter = std::max(maxIter_, 2048);
|
||||
|
||||
cb.xmin = (float)xmin_;
|
||||
cb.xmax = (float)xmax_;
|
||||
cb.ymin = (float)ymin_;
|
||||
cb.ymax = (float)ymax_;
|
||||
cb.width = imageWidth_;
|
||||
cb.height = imageHeight_;
|
||||
cb.maxIter = (colorScheme_ << 16) | (adaptiveMaxIter & 0xFFFF);
|
||||
cb.time = animTime_;
|
||||
}
|
||||
|
||||
// Update constant buffer
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
HRESULT hr = context_->Map(constantBuffer_.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
|
||||
if (SUCCEEDED(hr)) {
|
||||
memcpy(mapped.pData, &cb, sizeof(ConstantBuffer));
|
||||
context_->Unmap(constantBuffer_.Get(), 0);
|
||||
}
|
||||
|
||||
// Run compute shader
|
||||
context_->CSSetShader(computeShader_.Get(), nullptr, 0);
|
||||
context_->CSSetConstantBuffers(0, 1, constantBuffer_.GetAddressOf());
|
||||
context_->CSSetUnorderedAccessViews(0, 1, outputUAV_.GetAddressOf(), nullptr);
|
||||
|
||||
UINT dispatchX = (imageWidth_ + 7) / 8;
|
||||
UINT dispatchY = (imageHeight_ + 7) / 8;
|
||||
context_->Dispatch(dispatchX, dispatchY, 1);
|
||||
|
||||
// Clear compute shader bindings
|
||||
ID3D11UnorderedAccessView* nullUAV[] = { nullptr };
|
||||
context_->CSSetUnorderedAccessViews(0, 1, nullUAV, nullptr);
|
||||
context_->CSSetShader(nullptr, nullptr, 0);
|
||||
|
||||
// Clear render target
|
||||
float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
context_->ClearRenderTargetView(renderTargetView_.Get(), clearColor);
|
||||
|
||||
// Set viewport
|
||||
D3D11_VIEWPORT vp = {};
|
||||
vp.Width = (float)imageWidth_;
|
||||
vp.Height = (float)imageHeight_;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
context_->RSSetViewports(1, &vp);
|
||||
|
||||
// Render to screen
|
||||
context_->OMSetRenderTargets(1, renderTargetView_.GetAddressOf(), nullptr);
|
||||
context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
context_->VSSetShader(vertexShader_.Get(), nullptr, 0);
|
||||
context_->PSSetShader(pixelShader_.Get(), nullptr, 0);
|
||||
context_->PSSetShaderResources(0, 1, outputSRV_.GetAddressOf());
|
||||
context_->PSSetSamplers(0, 1, samplerState_.GetAddressOf());
|
||||
context_->Draw(3, 0);
|
||||
|
||||
swapChain_->Present(1, 0);
|
||||
|
||||
if (animationEnabled_) {
|
||||
animTime_ += 0.016f;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK MandelbrotApp::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
MandelbrotApp* pApp = nullptr;
|
||||
|
||||
if (msg == WM_NCCREATE) {
|
||||
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
|
||||
pApp = reinterpret_cast<MandelbrotApp*>(pCreate->lpCreateParams);
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pApp));
|
||||
}
|
||||
else {
|
||||
pApp = reinterpret_cast<MandelbrotApp*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
if (pApp) {
|
||||
return pApp->HandleMessage(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT MandelbrotApp::HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (msg) {
|
||||
case WM_SIZE: {
|
||||
if (swapChain_ && wParam != SIZE_MINIMIZED) {
|
||||
RECT rc;
|
||||
GetClientRect(hwnd, &rc);
|
||||
int width = rc.right - rc.left;
|
||||
int height = rc.bottom - rc.top;
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
context_->OMSetRenderTargets(0, nullptr, nullptr);
|
||||
renderTargetView_.Reset();
|
||||
|
||||
HRESULT hr = swapChain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ComPtr<ID3D11Texture2D> backBuffer;
|
||||
hr = swapChain_->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
|
||||
if (SUCCEEDED(hr)) {
|
||||
device_->CreateRenderTargetView(backBuffer.Get(), nullptr, &renderTargetView_);
|
||||
ResizeBuffers(width, height);
|
||||
|
||||
if (context_ && renderTargetView_) {
|
||||
context_->OMSetRenderTargets(1, renderTargetView_.GetAddressOf(), nullptr);
|
||||
|
||||
// Reset viewport
|
||||
D3D11_VIEWPORT vp;
|
||||
vp.Width = (float)width;
|
||||
vp.Height = (float)height;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = 0;
|
||||
vp.TopLeftY = 0;
|
||||
context_->RSSetViewports(1, &vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
|
||||
case WM_MOUSEWHEEL: {
|
||||
int delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
||||
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
||||
ScreenToClient(hwnd, &pt);
|
||||
|
||||
bool shiftPressed = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||
double zoomFactor = shiftPressed ?
|
||||
(delta > 0 ? 0.5 : 2.0) :
|
||||
(delta > 0 ? 0.8 : 1.25);
|
||||
|
||||
Zoom(zoomFactor, pt.x, pt.y);
|
||||
UpdateTitle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_RBUTTONDOWN: {
|
||||
SetCapture(hwnd);
|
||||
lastMouseX_ = GET_X_LPARAM(lParam);
|
||||
lastMouseY_ = GET_Y_LPARAM(lParam);
|
||||
rightDragging_ = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_RBUTTONUP: {
|
||||
if (rightDragging_) {
|
||||
int currentX = GET_X_LPARAM(lParam);
|
||||
int currentY = GET_Y_LPARAM(lParam);
|
||||
|
||||
if (abs(currentX - lastMouseX_) < 5 && abs(currentY - lastMouseY_) < 5) {
|
||||
ResetView();
|
||||
}
|
||||
}
|
||||
rightDragging_ = false;
|
||||
ReleaseCapture();
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_LBUTTONDOWN: {
|
||||
SetCapture(hwnd);
|
||||
lastMouseX_ = GET_X_LPARAM(lParam);
|
||||
lastMouseY_ = GET_Y_LPARAM(lParam);
|
||||
dragging_ = true;
|
||||
SetCursor(LoadCursor(nullptr, IDC_SIZEALL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_LBUTTONUP: {
|
||||
dragging_ = false;
|
||||
ReleaseCapture();
|
||||
SetCursor(LoadCursor(nullptr, IDC_ARROW));
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_MBUTTONDOWN: {
|
||||
int x = GET_X_LPARAM(lParam);
|
||||
int y = GET_Y_LPARAM(lParam);
|
||||
Zoom(1.0, x, y);
|
||||
UpdateTitle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_MOUSEMOVE: {
|
||||
if (dragging_) {
|
||||
int currentX = GET_X_LPARAM(lParam);
|
||||
int currentY = GET_Y_LPARAM(lParam);
|
||||
int dx = currentX - lastMouseX_;
|
||||
int dy = currentY - lastMouseY_;
|
||||
|
||||
if (dx != 0 || dy != 0) {
|
||||
Pan(dx, dy);
|
||||
lastMouseX_ = currentX;
|
||||
lastMouseY_ = currentY;
|
||||
}
|
||||
}
|
||||
else if (rightDragging_) {
|
||||
int currentY = GET_Y_LPARAM(lParam);
|
||||
int dy = currentY - lastMouseY_;
|
||||
|
||||
if (abs(dy) > 2) {
|
||||
AdjustIterations(-dy * 5);
|
||||
lastMouseY_ = currentY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_KEYDOWN: {
|
||||
keysPressed_[wParam] = true;
|
||||
|
||||
switch (wParam) {
|
||||
case 'H': {
|
||||
const wchar_t* helpText =
|
||||
L"MANDELBROT VIEWER CONTROLS\n\n"
|
||||
L"Mouse:\n"
|
||||
L" Left Drag - Pan view\n"
|
||||
L" Wheel - Zoom in/out\n"
|
||||
L" Shift+Wheel - Fast zoom\n"
|
||||
L" Middle Click - Center on point\n"
|
||||
L" Right Drag - Adjust iterations\n"
|
||||
L" Right Click - Reset view\n\n"
|
||||
L"Keyboard:\n"
|
||||
L" Arrow Keys - Pan view\n"
|
||||
L" Shift+Arrows - Fast pan\n"
|
||||
L" +/= / - - Zoom in/out\n"
|
||||
L" [ / ] - Adjust iterations\n"
|
||||
L" C - Cycle color schemes (5 total)\n"
|
||||
L" A - Toggle animation\n"
|
||||
L" R - Reset view\n"
|
||||
L" S - Save bookmark\n"
|
||||
L" 0-9 - Load bookmark\n"
|
||||
L" F11 - Toggle fullscreen\n"
|
||||
L" ESC - Exit fullscreen\n"
|
||||
L" H - This help";
|
||||
MessageBox(hwnd, helpText, L"Help", MB_OK | MB_ICONINFORMATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case 'R':
|
||||
ResetView();
|
||||
return 0;
|
||||
|
||||
case 'C':
|
||||
CycleColorScheme();
|
||||
return 0;
|
||||
|
||||
case 'A':
|
||||
ToggleAnimation();
|
||||
return 0;
|
||||
|
||||
case 'S':
|
||||
SaveBookmark();
|
||||
return 0;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
LoadBookmark(wParam - '0');
|
||||
return 0;
|
||||
|
||||
case VK_OEM_PLUS:
|
||||
case VK_ADD:
|
||||
Zoom(0.8, imageWidth_ / 2, imageHeight_ / 2);
|
||||
UpdateTitle();
|
||||
return 0;
|
||||
|
||||
case VK_OEM_MINUS:
|
||||
case VK_SUBTRACT:
|
||||
Zoom(1.25, imageWidth_ / 2, imageHeight_ / 2);
|
||||
UpdateTitle();
|
||||
return 0;
|
||||
|
||||
case 'D': // [
|
||||
AdjustIterations(-64);
|
||||
return 0;
|
||||
|
||||
case 'I': // ]
|
||||
AdjustIterations(64);
|
||||
return 0;
|
||||
|
||||
case VK_F11: {
|
||||
static bool fullscreen = false;
|
||||
static RECT savedRect;
|
||||
static DWORD savedStyle;
|
||||
|
||||
if (!fullscreen) {
|
||||
GetWindowRect(hwnd, &savedRect);
|
||||
savedStyle = GetWindowLong(hwnd, GWL_STYLE);
|
||||
|
||||
SetWindowLong(hwnd, GWL_STYLE, savedStyle & ~(WS_CAPTION | WS_THICKFRAME));
|
||||
|
||||
MONITORINFO mi = { sizeof(mi) };
|
||||
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &mi);
|
||||
SetWindowPos(hwnd, HWND_TOP,
|
||||
mi.rcMonitor.left, mi.rcMonitor.top,
|
||||
mi.rcMonitor.right - mi.rcMonitor.left,
|
||||
mi.rcMonitor.bottom - mi.rcMonitor.top,
|
||||
SWP_FRAMECHANGED);
|
||||
fullscreen = true;
|
||||
}
|
||||
else {
|
||||
SetWindowLong(hwnd, GWL_STYLE, savedStyle);
|
||||
SetWindowPos(hwnd, nullptr,
|
||||
savedRect.left, savedRect.top,
|
||||
savedRect.right - savedRect.left,
|
||||
savedRect.bottom - savedRect.top,
|
||||
SWP_FRAMECHANGED);
|
||||
fullscreen = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case VK_ESCAPE: {
|
||||
DWORD currentStyle = GetWindowLong(hwnd, GWL_STYLE);
|
||||
if (!(currentStyle & WS_CAPTION)) {
|
||||
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
|
||||
SetWindowPos(hwnd, nullptr, 100, 100, 1280, 720, SWP_FRAMECHANGED);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case VK_LEFT:
|
||||
case VK_RIGHT:
|
||||
case VK_UP:
|
||||
case VK_DOWN: {
|
||||
bool shift = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||
double speed = shift ? 50 : 10;
|
||||
|
||||
int dx = 0, dy = 0;
|
||||
if (wParam == VK_LEFT) dx = (int)speed;
|
||||
if (wParam == VK_RIGHT) dx = -(int)speed;
|
||||
if (wParam == VK_UP) dy = (int)speed;
|
||||
if (wParam == VK_DOWN) dy = -(int)speed;
|
||||
|
||||
Pan(dx, dy);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_KEYUP:
|
||||
keysPressed_[wParam] = false;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
void MandelbrotApp::Run() {
|
||||
MSG msg = {};
|
||||
LARGE_INTEGER lastTime;
|
||||
QueryPerformanceCounter(&lastTime);
|
||||
|
||||
double targetFrameTime = 1.0 / 120.0; // 60 FPS
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
|
||||
while (true) {
|
||||
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT)
|
||||
break;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
else {
|
||||
RenderMandelbrot();
|
||||
|
||||
// Limit frame rate
|
||||
LARGE_INTEGER currentTime;
|
||||
QueryPerformanceCounter(¤tTime);
|
||||
double elapsed = double(currentTime.QuadPart - lastTime.QuadPart) / freq.QuadPart;
|
||||
|
||||
if (elapsed < targetFrameTime) {
|
||||
DWORD sleepMs = DWORD((targetFrameTime - elapsed) * 1000);
|
||||
if (sleepMs > 0) Sleep(sleepMs);
|
||||
}
|
||||
QueryPerformanceCounter(&lastTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
simulations/mandelbrotset/cpp/mandelbrot_app.h
Normal file
91
simulations/mandelbrotset/cpp/mandelbrot_app.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef MANDELBROT_APP_H
|
||||
#define MANDELBROT_APP_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <wrl/client.h>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
struct ConstantBuffer {
|
||||
float xmin, xmax, ymin, ymax;
|
||||
int width, height, maxIter;
|
||||
float time;
|
||||
float padding[3];
|
||||
};
|
||||
|
||||
struct Bookmark {
|
||||
double xmin, xmax, ymin, ymax;
|
||||
int maxIter;
|
||||
bool saved = false;
|
||||
};
|
||||
|
||||
class MandelbrotApp {
|
||||
public:
|
||||
MandelbrotApp(HINSTANCE hInstance);
|
||||
~MandelbrotApp();
|
||||
void Run();
|
||||
|
||||
private:
|
||||
void CreateMainWindow();
|
||||
void InitD3D();
|
||||
void CreateComputeShader();
|
||||
void ResizeBuffers(int width, int height);
|
||||
void RenderMandelbrot();
|
||||
void UpdateTitle();
|
||||
|
||||
void Zoom(double factor, int centerX, int centerY);
|
||||
void Pan(int dx, int dy);
|
||||
void ResetView();
|
||||
void AdjustIterations(int delta);
|
||||
void CycleColorScheme();
|
||||
void ToggleAnimation();
|
||||
|
||||
void SaveBookmark();
|
||||
void LoadBookmark(int slot);
|
||||
|
||||
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
HINSTANCE hInstance_;
|
||||
HWND hwnd_;
|
||||
|
||||
ComPtr<ID3D11Device> device_;
|
||||
ComPtr<ID3D11DeviceContext> context_;
|
||||
ComPtr<IDXGISwapChain> swapChain_;
|
||||
ComPtr<ID3D11RenderTargetView> renderTargetView_;
|
||||
ComPtr<ID3D11ComputeShader> computeShader_;
|
||||
ComPtr<ID3D11VertexShader> vertexShader_;
|
||||
ComPtr<ID3D11PixelShader> pixelShader_;
|
||||
ComPtr<ID3D11Buffer> constantBuffer_;
|
||||
ComPtr<ID3D11Texture2D> outputTexture_;
|
||||
ComPtr<ID3D11UnorderedAccessView> outputUAV_;
|
||||
ComPtr<ID3D11ShaderResourceView> outputSRV_;
|
||||
ComPtr<ID3D11SamplerState> samplerState_;
|
||||
|
||||
double xmin_ = -2.5, xmax_ = 1.5;
|
||||
double ymin_ = -1.5, ymax_ = 1.5;
|
||||
int maxIter_ = 256;
|
||||
int colorScheme_ = 0;
|
||||
bool animationEnabled_ = false;
|
||||
float animTime_ = 0.0f;
|
||||
|
||||
int imageWidth_ = 0;
|
||||
int imageHeight_ = 0;
|
||||
int lastMouseX_ = 0;
|
||||
int lastMouseY_ = 0;
|
||||
bool dragging_ = false;
|
||||
bool rightDragging_ = false;
|
||||
std::unordered_map<WPARAM, bool> keysPressed_;
|
||||
std::mutex viewMutex_;
|
||||
|
||||
LARGE_INTEGER perfFreq_;
|
||||
LARGE_INTEGER lastFrameTime_;
|
||||
Bookmark bookmarks_[10];
|
||||
};
|
||||
|
||||
#endif
|
||||
19
simulations/mandelbrotset/generate.py
Normal file
19
simulations/mandelbrotset/generate.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from numba import njit
|
||||
import numpy as np
|
||||
|
||||
@njit
|
||||
def generate_mandelbrot(xmin, xmax, ymin, ymax, width, height, max_iter):
|
||||
img = np.zeros((height, width), dtype=np.uint32)
|
||||
for i in range(height):
|
||||
for j in range(width):
|
||||
x0 = xmin + (xmax - xmin) * j / width
|
||||
y0 = ymin + (ymax - ymin) * i / height
|
||||
x, y = 0.0, 0.0
|
||||
iter = 0
|
||||
while x*x + y*y <= 4.0 and iter < max_iter:
|
||||
xtemp = x*x - y*y + x0
|
||||
y = 2*x*y + y0
|
||||
x = xtemp
|
||||
iter += 1
|
||||
img[i, j] = iter
|
||||
return img
|
||||
95
simulations/mandelbrotset/mandelbrotset.py
Normal file
95
simulations/mandelbrotset/mandelbrotset.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from PIL import Image, ImageTk
|
||||
import threading
|
||||
import numpy as np
|
||||
import generate # your mandelbrot_numba / generate_mandelbrot module
|
||||
|
||||
class MandelbrotApp:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("Mandelbrot Viewer")
|
||||
|
||||
self.frame = ttk.Frame(root)
|
||||
self.frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
self.canvas = tk.Canvas(self.frame, bg="black", scrollregion=(0, 0, 2000, 2000))
|
||||
self.hbar = ttk.Scrollbar(self.frame, orient=tk.HORIZONTAL, command=self.canvas.xview)
|
||||
self.vbar = ttk.Scrollbar(self.frame, orient=tk.VERTICAL, command=self.canvas.yview)
|
||||
self.canvas.config(xscrollcommand=self.hbar.set, yscrollcommand=self.vbar.set)
|
||||
|
||||
self.hbar.pack(side=tk.BOTTOM, fill=tk.X)
|
||||
self.vbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
|
||||
# Mandelbrot view window
|
||||
self.xmin, self.xmax = -2.0, 1.0
|
||||
self.ymin, self.ymax = -1.5, 1.5
|
||||
self.zoom_factor = 1.0
|
||||
self.image_on_canvas = None
|
||||
|
||||
self.render_image()
|
||||
|
||||
# Mouse bindings
|
||||
self.canvas.bind("<MouseWheel>", self.zoom)
|
||||
self.canvas.bind("<Button-1>", self.drag_start)
|
||||
self.canvas.bind("<B1-Motion>", self.drag_motion)
|
||||
|
||||
def render_image(self):
|
||||
width, height = 800, 600
|
||||
max_iter = 200
|
||||
|
||||
def render_task():
|
||||
# Generate the Mandelbrot array
|
||||
img_array = generate.generate_mandelbrot(
|
||||
self.xmin, self.xmax, self.ymin, self.ymax,
|
||||
width, height, max_iter
|
||||
)
|
||||
|
||||
# Convert NumPy array to RGB image
|
||||
img_rgb = np.zeros((height, width, 3), dtype=np.uint8)
|
||||
img_rgb[:, :, 0] = img_array % 256
|
||||
img_rgb[:, :, 1] = (img_array * 2) % 256
|
||||
img_rgb[:, :, 2] = (img_array * 3) % 256
|
||||
|
||||
# Convert NumPy array → PIL Image → ImageTk.PhotoImage
|
||||
img = Image.fromarray(img_rgb)
|
||||
self.tk_img = ImageTk.PhotoImage(img)
|
||||
|
||||
# Update canvas
|
||||
self.canvas.delete("all")
|
||||
self.image_on_canvas = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_img)
|
||||
self.canvas.config(scrollregion=(0, 0, width, height))
|
||||
|
||||
threading.Thread(target=render_task, daemon=True).start()
|
||||
|
||||
def zoom(self, event):
|
||||
zoom_amount = 0.9 if event.delta > 0 else 1.1
|
||||
self.zoom_factor *= zoom_amount
|
||||
center_x = (self.xmin + self.xmax) / 2
|
||||
center_y = (self.ymin + self.ymax) / 2
|
||||
width = (self.xmax - self.xmin) * zoom_amount
|
||||
height = (self.ymax - self.ymin) * zoom_amount
|
||||
self.xmin, self.xmax = center_x - width / 2, center_x + width / 2
|
||||
self.ymin, self.ymax = center_y - height / 2, center_y + height / 2
|
||||
self.render_image()
|
||||
|
||||
def drag_start(self, event):
|
||||
self.last_x, self.last_y = event.x, event.y
|
||||
|
||||
def drag_motion(self, event):
|
||||
dx = event.x - self.last_x
|
||||
dy = event.y - self.last_y
|
||||
x_shift = (self.xmax - self.xmin) * dx / 800
|
||||
y_shift = (self.ymax - self.ymin) * dy / 600
|
||||
self.xmin -= x_shift
|
||||
self.xmax -= x_shift
|
||||
self.ymin += y_shift
|
||||
self.ymax += y_shift
|
||||
self.last_x, self.last_y = event.x, event.y
|
||||
self.render_image()
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = MandelbrotApp(root)
|
||||
root.mainloop()
|
||||
247
simulations/mandelbrotset/mandelbrotsetCPP.cpp
Normal file
247
simulations/mandelbrotset/mandelbrotsetCPP.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
#ifndef _UNICODE
|
||||
#define _UNICODE
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
// Window size
|
||||
const int WIDTH = 800;
|
||||
const int HEIGHT = 600;
|
||||
|
||||
// Mandelbrot view
|
||||
double xmin = -2.0, xmax = 1.0;
|
||||
double ymin = -1.5, ymax = 1.5;
|
||||
int max_iter = 500;
|
||||
|
||||
// Pixel buffer
|
||||
std::vector<unsigned char> pixels(WIDTH* HEIGHT * 3);
|
||||
HBITMAP hBitmap = nullptr;
|
||||
std::mutex renderMutex;
|
||||
|
||||
// Track ongoing rendering
|
||||
std::atomic<bool> rendering(false);
|
||||
|
||||
// Low-res rendering factor
|
||||
int previewScale = 4;
|
||||
|
||||
// Forward declarations
|
||||
void generateMandelbrot(int width, int height, std::vector<unsigned char>& buffer);
|
||||
|
||||
// Create or update the bitmap
|
||||
void updateBitmap(HDC hdc)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(renderMutex);
|
||||
|
||||
BITMAPINFO bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = WIDTH;
|
||||
bmi.bmiHeader.biHeight = -HEIGHT; // top-down
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 24;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
if (!hBitmap)
|
||||
{
|
||||
void* pBits;
|
||||
hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
|
||||
}
|
||||
|
||||
SetDIBits(hdc, hBitmap, 0, HEIGHT, pixels.data(), &bmi, DIB_RGB_COLORS);
|
||||
}
|
||||
|
||||
// Multi-threaded block render
|
||||
void renderBlock(int yStart, int yEnd, int width, int height, std::vector<unsigned char>& buffer)
|
||||
{
|
||||
for (int py = yStart; py < yEnd; ++py)
|
||||
{
|
||||
double y0 = ymin + (ymax - ymin) * py / height;
|
||||
for (int px = 0; px < width; ++px)
|
||||
{
|
||||
double x0 = xmin + (xmax - xmin) * px / width;
|
||||
std::complex<double> c(x0, y0), z(0, 0);
|
||||
int iter = 0;
|
||||
while (std::abs(z) <= 2.0 && iter < max_iter) z = z * z + c, iter++;
|
||||
|
||||
int idx = (py * width + px) * 3;
|
||||
buffer[idx + 0] = iter % 256;
|
||||
buffer[idx + 1] = (iter * 2) % 256;
|
||||
buffer[idx + 2] = (iter * 3) % 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-threaded Mandelbrot
|
||||
void generateMandelbrot(int width, int height, std::vector<unsigned char>& buffer)
|
||||
{
|
||||
int numThreads = std::thread::hardware_concurrency();
|
||||
std::vector<std::thread> threads;
|
||||
int blockHeight = height / numThreads;
|
||||
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
int yStart = i * blockHeight;
|
||||
int yEnd = (i == numThreads - 1) ? height : yStart + blockHeight;
|
||||
threads.emplace_back(renderBlock, yStart, yEnd, width, height, std::ref(buffer));
|
||||
}
|
||||
|
||||
for (auto& t : threads) t.join();
|
||||
}
|
||||
|
||||
// Start rendering in a background thread
|
||||
void startRender(HWND hwnd)
|
||||
{
|
||||
if (rendering) return; // avoid multiple renders
|
||||
rendering = true;
|
||||
|
||||
std::thread([hwnd]()
|
||||
{
|
||||
// 1) Low-res preview
|
||||
int lowW = WIDTH / previewScale, lowH = HEIGHT / previewScale;
|
||||
std::vector<unsigned char> preview(lowW * lowH * 3);
|
||||
generateMandelbrot(lowW, lowH, preview);
|
||||
|
||||
// Upscale preview to full size
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(renderMutex);
|
||||
for (int y = 0;y < HEIGHT;y++)
|
||||
for (int x = 0;x < WIDTH;x++)
|
||||
{
|
||||
int px = x / previewScale;
|
||||
int py = y / previewScale;
|
||||
int idx = (y * WIDTH + x) * 3;
|
||||
int idxSmall = (py * lowW + px) * 3;
|
||||
pixels[idx + 0] = preview[idxSmall + 0];
|
||||
pixels[idx + 1] = preview[idxSmall + 1];
|
||||
pixels[idx + 2] = preview[idxSmall + 2];
|
||||
}
|
||||
}
|
||||
InvalidateRect(hwnd, nullptr, TRUE);
|
||||
|
||||
// 2) Full-res render
|
||||
generateMandelbrot(WIDTH, HEIGHT, pixels);
|
||||
InvalidateRect(hwnd, nullptr, TRUE);
|
||||
|
||||
rendering = false;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
// Dragging
|
||||
int lastX = 0, lastY = 0;
|
||||
bool dragging = false;
|
||||
|
||||
// Window procedure
|
||||
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(hwnd, &ps);
|
||||
updateBitmap(hdc);
|
||||
|
||||
HDC memDC = CreateCompatibleDC(hdc);
|
||||
HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, hBitmap);
|
||||
BitBlt(hdc, 0, 0, WIDTH, HEIGHT, memDC, 0, 0, SRCCOPY);
|
||||
SelectObject(memDC, oldBmp);
|
||||
DeleteDC(memDC);
|
||||
|
||||
EndPaint(hwnd, &ps);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
short delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
||||
double zoomFactor = (delta > 0) ? 0.8 : 1.25;
|
||||
|
||||
double centerX = (xmin + xmax) / 2;
|
||||
double centerY = (ymin + ymax) / 2;
|
||||
double width = (xmax - xmin) * zoomFactor;
|
||||
double height = (ymax - ymin) * zoomFactor;
|
||||
|
||||
xmin = centerX - width / 2;
|
||||
xmax = centerX + width / 2;
|
||||
ymin = centerY - height / 2;
|
||||
ymax = centerY + height / 2;
|
||||
|
||||
startRender(hwnd);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
dragging = true;
|
||||
lastX = LOWORD(lParam);
|
||||
lastY = HIWORD(lParam);
|
||||
return 0;
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
dragging = false;
|
||||
return 0;
|
||||
|
||||
case WM_MOUSEMOVE:
|
||||
if (dragging)
|
||||
{
|
||||
int x = LOWORD(lParam);
|
||||
int y = HIWORD(lParam);
|
||||
double dx = (x - lastX) * (xmax - xmin) / WIDTH;
|
||||
double dy = (y - lastY) * (ymax - ymin) / HEIGHT;
|
||||
|
||||
xmin -= dx; xmax -= dx;
|
||||
ymin += dy; ymax += dy;
|
||||
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
|
||||
startRender(hwnd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
// Entry point
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
|
||||
{
|
||||
const wchar_t CLASS_NAME[] = L"MandelbrotWindow";
|
||||
|
||||
WNDCLASS wc = {};
|
||||
wc.lpfnWndProc = WindowProc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = CLASS_NAME;
|
||||
RegisterClass(&wc);
|
||||
|
||||
HWND hwnd = CreateWindowEx(
|
||||
0, CLASS_NAME, L"Mandelbrot Explorer", WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT,
|
||||
nullptr, nullptr, hInstance, nullptr
|
||||
);
|
||||
|
||||
ShowWindow(hwnd, nCmdShow);
|
||||
|
||||
// Initial render
|
||||
startRender(hwnd);
|
||||
|
||||
MSG msg = {};
|
||||
while (GetMessage(&msg, nullptr, 0, 0))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
92
simulations/pendulum/main.py
Normal file
92
simulations/pendulum/main.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import pygame
|
||||
import math
|
||||
|
||||
# Initialize Pygame
|
||||
pygame.init()
|
||||
|
||||
# Screen setup
|
||||
WIDTH, HEIGHT = 800, 600
|
||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||
pygame.display.set_caption("Interactive Adjustable Pendulum")
|
||||
|
||||
# Colors
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
RED = (200, 50, 50)
|
||||
|
||||
# Clock for FPS
|
||||
clock = pygame.time.Clock()
|
||||
FPS = 60
|
||||
|
||||
# Pendulum properties
|
||||
origin = (WIDTH // 2, 100)
|
||||
length = 300
|
||||
angle = math.pi / 4
|
||||
angular_velocity = 0
|
||||
angular_acceleration = 0
|
||||
gravity = 0.8
|
||||
dragging = False
|
||||
drag_padding = 30 # extra radius for easier dragging
|
||||
|
||||
def get_bob_position(angle):
|
||||
x = origin[0] + length * math.sin(angle)
|
||||
y = origin[1] + length * math.cos(angle)
|
||||
return int(x), int(y)
|
||||
|
||||
bob_pos = get_bob_position(angle)
|
||||
|
||||
running = True
|
||||
while running:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
# Start dragging
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
mx, my = event.pos
|
||||
if math.hypot(mx - bob_pos[0], my - bob_pos[1]) < 20 + drag_padding:
|
||||
dragging = True
|
||||
# Stop dragging
|
||||
elif event.type == pygame.MOUSEBUTTONUP:
|
||||
dragging = False
|
||||
|
||||
# Keyboard controls
|
||||
keys = pygame.key.get_pressed()
|
||||
if keys[pygame.K_UP]:
|
||||
length += 1 # increase pendulum length
|
||||
if keys[pygame.K_DOWN]:
|
||||
length = max(50, length - 1) # decrease length, min 50
|
||||
if keys[pygame.K_RIGHT]:
|
||||
gravity += 0.01
|
||||
if keys[pygame.K_LEFT]:
|
||||
gravity = max(0.1, gravity - 0.01)
|
||||
|
||||
if dragging:
|
||||
mx, my = pygame.mouse.get_pos()
|
||||
dx = mx - origin[0]
|
||||
dy = my - origin[1]
|
||||
angle = math.atan2(dx, dy)
|
||||
angular_velocity = 0
|
||||
bob_pos = get_bob_position(angle)
|
||||
else:
|
||||
# Physics
|
||||
angular_acceleration = (-gravity / length) * math.sin(angle)
|
||||
angular_velocity += angular_acceleration
|
||||
angle += angular_velocity
|
||||
angular_velocity *= 0.99 # damping
|
||||
bob_pos = get_bob_position(angle)
|
||||
|
||||
# Drawing
|
||||
screen.fill(BLACK)
|
||||
pygame.draw.line(screen, WHITE, origin, bob_pos, 3)
|
||||
pygame.draw.circle(screen, RED, bob_pos, 20)
|
||||
pygame.draw.circle(screen, WHITE, origin, 5)
|
||||
|
||||
# Display info
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
info = font.render(f"Length: {int(length)} Gravity: {gravity:.2f}", True, WHITE)
|
||||
screen.blit(info, (10, 10))
|
||||
|
||||
pygame.display.flip()
|
||||
clock.tick(FPS)
|
||||
|
||||
pygame.quit()
|
||||
10
simulations/sorting_quicksort/setup.py
Normal file
10
simulations/sorting_quicksort/setup.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from setuptools import setup, Extension
|
||||
|
||||
module = Extension("sorting", sources=["sorting.c"])
|
||||
|
||||
setup(
|
||||
name="sorting",
|
||||
version="1.0",
|
||||
description="Simple C sorting extension",
|
||||
ext_modules=[module],
|
||||
)
|
||||
48
simulations/sorting_quicksort/sorting.c
Normal file
48
simulations/sorting_quicksort/sorting.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <Python.h>
|
||||
|
||||
// The actual sorting implementation
|
||||
static PyObject* sorting_sort(PyObject* self, PyObject* args) {
|
||||
PyObject* list;
|
||||
if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &list))
|
||||
return NULL;
|
||||
|
||||
Py_ssize_t n = PyList_Size(list);
|
||||
for (Py_ssize_t i = 0; i < n - 1; i++) {
|
||||
for (Py_ssize_t j = 0; j < n - i - 1; j++) {
|
||||
PyObject* a = PyList_GetItem(list, j);
|
||||
PyObject* b = PyList_GetItem(list, j + 1);
|
||||
|
||||
int cmp = PyObject_RichCompareBool(a, b, Py_GT);
|
||||
if (cmp == -1) return NULL;
|
||||
if (cmp) {
|
||||
Py_INCREF(a);
|
||||
Py_INCREF(b);
|
||||
PyList_SetItem(list, j, b);
|
||||
PyList_SetItem(list, j + 1, a);
|
||||
Py_DECREF(a);
|
||||
Py_DECREF(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// Table of functions to export
|
||||
static PyMethodDef SortingMethods[] = {
|
||||
{"sort", sorting_sort, METH_VARARGS, "Sort a Python list in place."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
// Module definition
|
||||
static struct PyModuleDef sortingmodule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"sorting",
|
||||
NULL,
|
||||
-1,
|
||||
SortingMethods
|
||||
};
|
||||
|
||||
// Module initialization
|
||||
PyMODINIT_FUNC PyInit_sorting(void) {
|
||||
return PyModule_Create(&sortingmodule);
|
||||
}
|
||||
4
simulations/sorting_quicksort/sorting.py
Normal file
4
simulations/sorting_quicksort/sorting.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import sorting
|
||||
arr = [5, 3, 8, 1, 2]
|
||||
sorting.sorting(arr)
|
||||
print(arr)
|
||||
13
stmkbezirke/main.py
Normal file
13
stmkbezirke/main.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import random
|
||||
|
||||
bezirke = [
|
||||
"Graz", "Graz-Umgebung", "Leibnitz", "Deutschlandsberg",
|
||||
"Voitsberg", "Weiz", "Hartberg-Fürstenfeld", "Südoststeiermark",
|
||||
"Murau", "Murtal", "Liezen", "Bruck-Mürzzuschlag", "Leoben"
|
||||
]
|
||||
|
||||
gewinner = random.sample(bezirke, 5)
|
||||
|
||||
print("Die 5 geförderten Bezirke sind:")
|
||||
for bezirk in gewinner:
|
||||
print("-", bezirk)
|
||||
Reference in New Issue
Block a user