made several changes working on zoom

added proper particle count
there is a settings menu
some keybindings have been added ESC to exit
Glowing particles are now a thing
Gas effects can be toggled on and off for performance
This commit is contained in:
Stan44 2024-12-26 19:16:46 -06:00
parent 05e37a0bd5
commit b9a280208b
8 changed files with 261 additions and 30 deletions

3
.gitignore vendored
View File

@ -163,3 +163,6 @@ cython_debug/
current_stations.html
forecast_data.json
openapi.json
__pycache__/sim.cpython-312.pyc
__pycache__/sim.cpython-312.pyc
__pycache__/rendering.cpython-312.pyc

Binary file not shown.

View File

@ -1,6 +1,7 @@
#File Name: rendering.py
from settings import pygame, random, particle_properties
from settings import pygame, random, particle_properties, engine_settings
class Rendering:
@ -69,31 +70,68 @@ class Rendering:
base_color = particle_colors.get(particle.particle_type, (255, 255, 255))
color = list(base_color)
if particle.is_gas:
# Enhanced gas visibility
alpha = random.randint(128, 200)
color = list(color)
if len(color) < 4:
color.append(alpha)
else:
color[3] = alpha
# Add subtle movement effect
offset_x = random.randint(-1, 1)
offset_y = random.randint(-1, 1)
rect = (x * particle_size + offset_x, y * particle_size + offset_y,
particle_size, particle_size)
else:
rect = (x * particle_size, y * particle_size,
particle_size, particle_size)
if engine_settings['enable_glow']:
glow_color = (255, 255, 255)
glow_radius = 0.5 * particle_size
glow_surface = pygame.Surface((glow_radius * 2, glow_radius * 2), pygame.SRCALPHA)
pygame.draw.circle(glow_surface, glow_color, (glow_radius, glow_radius), glow_radius)
glow_surface.set_alpha(100)
self.particle_surface.blit(glow_surface, (x * particle_size - glow_radius, y * particle_size - glow_radius))
if engine_settings['enable_gas_effect']:
if particle.is_gas:
# Enhanced gas visibility
alpha = random.randint(128, 200)
color = list(color)
if len(color) < 4:
color.append(alpha)
else:
color[3] = alpha
# Add subtle movement effect
offset_x = random.randint(-1, 1)
offset_y = random.randint(-1, 1)
rect = (x * particle_size + offset_x, y * particle_size + offset_y,
particle_size, particle_size)
rect = (x * particle_size, y * particle_size,
particle_size, particle_size)
pygame.draw.rect(self.particle_surface, color, rect)
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.particle_surface, (0, 0))
def draw_zoom_window(self, particles, particle_size, particle_colors, mouse_pos, zoom_factor=4):
print(f"Drawing zoom window.")
zoom_size = 100 # Size of zoom window
zoom_surface = pygame.Surface((zoom_size, zoom_size))
zoom_surface.fill((0, 0, 0))
# Get area around mouse to zoom
mouse_x, mouse_y = mouse_pos
view_x = mouse_x - zoom_size/(2*zoom_factor)
view_y = mouse_y - zoom_size/(2*zoom_factor)
print(f"Viewing area: {view_x}, {view_y}")
# Draw zoomed particles
for x in range(int(view_x), int(view_x + zoom_size/zoom_factor)):
for y in range(int(view_y), int(view_y + zoom_size/zoom_factor)):
if 0 <= x < len(particles) and 0 <= y < len(particles[0]):
particle = particles[x][y]
if particle:
color = particle_colors.get(particle.particle_type, (255, 255, 255))
rect = ((x - view_x) * zoom_factor,
(y - view_y) * zoom_factor,
particle_size * zoom_factor,
particle_size * zoom_factor)
pygame.draw.rect(zoom_surface, color, rect)
print(f"Drawing zoom window at {mouse_pos}{zoom_surface}")
return zoom_surface
def draw_debug_overlay(self, fps, particles): # this is the function that draws the debug overlay
# Get mouse position and convert to grid coordinates
mouse_x, mouse_y = pygame.mouse.get_pos()
@ -124,13 +162,16 @@ class Rendering:
# Draw debug information
font = pygame.font.SysFont(None, 24)
particle_count = sum(1 for row in particles for cell in row if cell is not None)
debug_info = [
f"FPS: {int(fps)}",
f"Mouse: ({mouse_x}, {mouse_y})",
f"Grid: ({grid_x}, {grid_y})",
f"Particle: {particle_info}"
f"Particle: {particle_info}",
#f"Active Particle Count: {sim.Simulation.get_active_particle_count(sim.Simulation)}",
f"Particle Count: {particle_count}"
]
y_offset = 10
for info in debug_info:
debug_text = font.render(info, True, (255, 255, 255))
@ -175,6 +216,13 @@ class Rendering:
label = font.render("Clear", True, (255, 255, 255))
self.screen.blit(label, (self.clear_screen_button.x + 5, self.clear_screen_button.y + 5))
# Draw Settings menu directly below the buttons
self.settings_button = pygame.Rect(x_offset, y_offset + 50, 80, 25)
pygame.draw.rect(self.screen, (255, 255, 255), self.settings_button)
font = pygame.font.SysFont(None, 24)
label = font.render("Settings", True, (0, 0, 0))
self.screen.blit(label, (self.settings_button.x + 5, self.settings_button.y + 5))
def render_brush_curser(self, x, y, radius): # this is the function that draws the brush curser but isn't used yet so unkown if works
# Draw a circle cursor for brushsize
@ -189,8 +237,27 @@ class Rendering:
label = pygame.font.SysFont(None, 24).render(f"Brush Size: {brush_size}", True, (255, 255, 255))
self.screen.blit(label, (500, 10))
def draw_settings_menu(self):
settings_surface = pygame.Surface((300, 400))
settings_surface.fill((50, 50, 50))
y_offset = 10
font = pygame.font.SysFont(None, 24)
for setting, value in engine_settings.items():
# Create toggle button
button_rect = pygame.Rect(10, y_offset, 20, 20)
pygame.draw.rect(settings_surface, (0, 255, 0) if value else (255, 0, 0), button_rect)
# Draw setting name
label = font.render(setting.replace('_', ' ').title(), True, (255, 255, 255))
settings_surface.blit(label, (40, y_offset))
y_offset += 30
return settings_surface
def clear_screen(self, sim): ## this is the function that clears the screen
def clear_screen(self, sim): # this is the function that clears the screen
# Store current particle type
current_type = sim.current_particle_type

View File

@ -5,27 +5,36 @@
# This is my most functional system for falling sand in python yet i took some things i learned in JS.
# This needs further optimizations to core performance sections.
from settings import pygame
from settings import pygame, engine_settings
from rendering import Rendering
from sim import Simulation
def main(): # Main function to run the program
pygame.init()
clock = pygame.time.Clock()
pygame.display.set_mode((1024, 768), pygame.HWSURFACE | pygame.DOUBLEBUF)
sim = Simulation(1024, 768)
rendering = Rendering(1024, 768)
width = 1024
height = 768
screen = pygame.display.set_mode((width, height), pygame.HWSURFACE | pygame.DOUBLEBUF)
sim = Simulation(width, height)
rendering = Rendering(width, height)
mouse_down_left = False
mouse_down_right = False
mouse_down_middle = False
mouse_down_wheel_up = False
mouse_down_wheel_down = False
over_button = False
zoom_active = False
zoom_locked = False
zoom_pos = None
settings_visible = False
settings_menu_y = 100
running = True
while running:
fps = clock.get_fps()
dt = clock.tick(60) / 1000
keys = pygame.key.get_pressed()
zoom_active = keys[pygame.K_z]
# Handle events
@ -38,6 +47,11 @@ def main(): # Main function to run the program
sim.brush_size = max(sim.brush_size - 1, 1)
elif event.button == 1: # Left click
over_button = False
if zoom_active:
zoom_locked = not zoom_locked
if zoom_locked:
zoom_pos = mouse_pos
# Check category buttons
for category, button in rendering.category_buttons.items():
@ -59,6 +73,18 @@ def main(): # Main function to run the program
rendering.clear_screen(sim)
over_button = True
if rendering.settings_button.collidepoint(event.pos):
settings_visible = not settings_visible
elif settings_visible:
# Handle settings toggles
mouse_pos = pygame.mouse.get_pos()
relative_y = mouse_pos[1] - settings_menu_y
setting_index = relative_y // 30
if 0 <= setting_index < len(engine_settings):
setting_name = list(engine_settings.keys())[setting_index]
engine_settings[setting_name] = not engine_settings[setting_name]
if not over_button:
mouse_down_left = True
@ -74,6 +100,23 @@ def main(): # Main function to run the program
mouse_down_right = False
elif event.type == pygame.MOUSEBUTTONUP and event.button == 2:
mouse_down_middle = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
print("Escape button pressed")
print(f"Exiting Program {__file__}")
running = False
elif event.key == pygame.K_SPACE:
print(f"Pause button pressed but not functional 'Space'")
pass
elif event.key == pygame.K_c:
rendering.clear_screen(sim)
elif event.key == pygame.K_z:
zoom_active = not zoom_active
if zoom_active:
zoom_locked = False
zoom_pos = pygame.mouse.get_pos()
elif event.type == pygame.QUIT:
running = False
@ -94,6 +137,17 @@ def main(): # Main function to run the program
rendering.draw_particles(sim.particles, sim.active_particles, sim.particle_size, rendering.particle_colors)
rendering.draw_buttons()
rendering.draw_brush_size_slider(sim.brush_size)
if zoom_active or zoom_locked:
mouse_pos = zoom_pos if zoom_locked else pygame.mouse.get_pos()
zoom_surface = rendering.draw_zoom_window(sim.particles, sim.particle_size, rendering.particle_colors, mouse_pos)
# Position zoom window
zoom_x = 10 if mouse_pos[0] > width/2 else width - 110
zoom_y = 10 if mouse_pos[1] > height/2 else height - 110
screen.blit(zoom_surface, (zoom_x, zoom_y))
if settings_visible:
settings_menu = rendering.draw_settings_menu()
rendering.screen.blit(settings_menu, (rendering.width - 320, 100))
rendering.draw_debug_overlay(fps, sim.particles)
pygame.display.flip()

View File

@ -6,6 +6,13 @@ import json
import random
import time
engine_settings = {
'enable_glow': True,
'enable_gas_effect': True
# 'settings': True/False
}
# Load particle properties from JSON file
def load_particle_properties():
try:
with open('particles.json') as f:
@ -16,3 +23,4 @@ def load_particle_properties():
# Load particle properties once when module is imported
particle_properties = load_particle_properties()

27
sim.py
View File

@ -100,10 +100,14 @@ class Simulation:
def update_spatial_grid(self): # this is where we update the spatial grid.
"""Update spatial grid for optimized collision detection"""
self.spatial_grid.clear()
for x, y in self.active_particles:
self.add_to_spatial_grid(self.particles[x][y], x, y)
if len(self.active_particles) > 100: # Threshold for rebuild
self.spatial_grid.clear()
for x, y in self.active_particles:
cell_key = self.get_cell_key(x, y)
if cell_key not in self.spatial_grid:
self.spatial_grid[cell_key] = set()
self.spatial_grid[cell_key].add((x, y))
def handle_phase_transitions(self, particle, x, y): # this is where we handle all the phase transitions.
@ -593,6 +597,21 @@ class Simulation:
liquid2.color = self.calculate_color(liquid2.temperature)
def get_particle_count(self):
"""Returns the number of particles in the simulation for the Debug display and for performance analysis."""
count = 0
for x in range(self.width):
for y in range(self.height):
if self.particles[x][y] is not None:
count += 1
return count
def get_active_particle_count(self):
"""Returns the number of active particles in the simulation for the Debug display and for performance analysis."""
pass
def simulate_step(self, dt):
"""Run a single step of the simulation"""
# Update particle positions and physics

80
template_particles.json Normal file
View File

@ -0,0 +1,80 @@
{
"Template": {
"description": "This is a template for particles.",
"description2": "Remove this for your own particle mods to work"
},
"sand": {
"name": "Sand",
"size": 1,
"hardness": 0.5,
"color": [255, 255, 0, 255],
"velocity": 0.5,
"mass": 0.5,
"conductivity": 0,
"heat_capacity": 1,
"flamability": 0.8,
"temperature": 0,
"explosive": false,
"explosion_radius": 0,
"explosion_color": [0, 0, 0],
"friction": 0.5,
"viscosity": 0,
"pressure": 0,
"melt": "molten-Glass",
"melt_temperature": 1000,
"conductive": false,
"liquid": false,
"solid": true,
"is_gas": false
},
"water": {
"name": "Water",
"size": 1,
"hardness": 0.2,
"velocity": 0.3,
"conductivity": 1,
"heat_capacity": 1,
"color": [0, 0, 255, 255],
"mass": 1,
"flamability": 0,
"temperature": 22,
"explosive": false,
"explosion_radius": 0,
"explosion_color": [0, 0, 0],
"friction": 1,
"viscosity": 1,
"pressure": 0.5,
"evaporate": "steam",
"evaporate_temperature": 145,
"freeze": "ice",
"freeze_temperature": 0,
"melt": "water",
"melt_temperature": 20,
"liquid": true,
"solid": false,
"is_gas": false
},
"steam": {
"name": "Steam",
"size": 1,
"hardness": 0.0,
"velocity": 0.2,
"conductivity": 1,
"heat_capacity": 1,
"color": [255, 255, 255, 255],
"mass": 0.01,
"flamability": 0,
"temperature": 100,
"solidify_temperature": 98,
"solidify": "water",
"explosive": false,
"explosion_radius": 0,
"explosion_color": [0, 0, 0],
"friction": 0.5,
"viscosity": 0.5,
"liquid": false,
"solid": false,
"is_gas": true,
"conductive": false
}
}