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:
parent
05e37a0bd5
commit
b9a280208b
3
.gitignore
vendored
3
.gitignore
vendored
@ -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.
Binary file not shown.
111
rendering.py
111
rendering.py
@ -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
|
||||
|
||||
|
||||
62
sandpypi.py
62
sandpypi.py
@ -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()
|
||||
|
||||
|
||||
@ -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
27
sim.py
@ -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
80
template_particles.json
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user