updated using numpy
debugger_system
This commit is contained in:
parent
dac5fa2213
commit
c6c047fdb7
@ -22,7 +22,7 @@ import time
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
engine_settings = {
|
engine_settings = {
|
||||||
'pause_sim': True,
|
'pause_sim': False,
|
||||||
'enable_cursor': True,
|
'enable_cursor': True,
|
||||||
'enable_glow': False,
|
'enable_glow': False,
|
||||||
'enable_gas_effect': True,
|
'enable_gas_effect': True,
|
||||||
@ -76,5 +76,5 @@ def load_particle_properties():
|
|||||||
# Load particle properties once when module is imported
|
# Load particle properties once when module is imported
|
||||||
particle_properties = load_particle_properties()
|
particle_properties = load_particle_properties()
|
||||||
|
|
||||||
__all__ = ['pygame', 'np', 'random', 'time', 'engine_settings', 'particle_properties', 'cProfile', 'pstats', 'sys', 'os']
|
__all__ = ['pygame', 'np', 'random', 'time', 'engine_settings', 'particle_properties', 'cProfile', 'pstats', 'sys', 'os', 'jit']
|
||||||
|
|
||||||
|
|||||||
66
src/debug/debugger_system.py
Normal file
66
src/debug/debugger_system.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
from config.settings import engine_settings, pygame
|
||||||
|
import time
|
||||||
|
|
||||||
|
class DebuggerSystem:
|
||||||
|
def __init__(self):
|
||||||
|
self.debug_surface = pygame.Surface((300, 150), pygame.SRCALPHA)
|
||||||
|
self.debug_font = pygame.font.SysFont(None, 24)
|
||||||
|
self.fps_counter = 0
|
||||||
|
self.fps_timer = time.time()
|
||||||
|
self.current_fps = 0
|
||||||
|
self.debug_text = []
|
||||||
|
self.performance_metrics = {
|
||||||
|
'particle_updates': 0,
|
||||||
|
'physics_time': 0,
|
||||||
|
'render_time': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
def track_fps(self):
|
||||||
|
"""Track FPS with high precision"""
|
||||||
|
self.fps_counter += 1
|
||||||
|
current_time = time.time()
|
||||||
|
elapsed = current_time - self.fps_timer
|
||||||
|
|
||||||
|
if elapsed >= 1.0:
|
||||||
|
self.current_fps = self.fps_counter / elapsed
|
||||||
|
self.fps_counter = 0
|
||||||
|
self.fps_timer = current_time
|
||||||
|
return self.current_fps
|
||||||
|
|
||||||
|
def update_performance_metrics(self, sim):
|
||||||
|
"""Track simulation performance metrics"""
|
||||||
|
self.performance_metrics['particle_updates'] = len(sim.active_particles)
|
||||||
|
return self.performance_metrics
|
||||||
|
|
||||||
|
def draw_debug_overlay(self, screen, sim):
|
||||||
|
if not engine_settings['enable_fps'] and not engine_settings['enable_debug']:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.debug_surface.fill((0, 0, 0, 128))
|
||||||
|
y_offset = 10
|
||||||
|
|
||||||
|
if engine_settings['enable_fps']:
|
||||||
|
fps_text = f"FPS: {self.current_fps:.1f} | TPS: {sim.track_tps():.1f}"
|
||||||
|
fps_surf = self.debug_font.render(fps_text, True, (255, 255, 255))
|
||||||
|
self.debug_surface.blit(fps_surf, (10, y_offset))
|
||||||
|
y_offset += 25
|
||||||
|
|
||||||
|
if engine_settings['enable_debug']:
|
||||||
|
debug_info = [
|
||||||
|
f"Active Particles: {len(sim.active_particles)}",
|
||||||
|
f"Total Particles: {sim.particle_count}",
|
||||||
|
f"Current Type: {sim.current_particle_type}",
|
||||||
|
f"Brush Size: {sim.brush_size}"
|
||||||
|
]
|
||||||
|
|
||||||
|
for line in debug_info:
|
||||||
|
text_surf = self.debug_font.render(line, True, (255, 255, 255))
|
||||||
|
self.debug_surface.blit(text_surf, (10, y_offset))
|
||||||
|
y_offset += 25
|
||||||
|
|
||||||
|
screen.blit(self.debug_surface, (0, 0))
|
||||||
|
|
||||||
|
def log_error(self, error_msg):
|
||||||
|
"""Log errors for debugging"""
|
||||||
|
with open('debug_log.txt', 'a') as f:
|
||||||
|
f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {error_msg}\n")
|
||||||
@ -19,7 +19,7 @@ Key Components:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#Load the imports.
|
#Load the imports.
|
||||||
from config.settings import random, time, particle_properties
|
from config.settings import np, time, particle_properties
|
||||||
|
|
||||||
|
|
||||||
# Load particle properties from json so we know what particles we got and how they should be simulated.
|
# Load particle properties from json so we know what particles we got and how they should be simulated.
|
||||||
@ -112,13 +112,24 @@ class Simulation:
|
|||||||
self.max_brush_size = 20
|
self.max_brush_size = 20
|
||||||
self.particle_properties = particle_properties
|
self.particle_properties = particle_properties
|
||||||
self.particle_types = list(self.particle_properties.keys())
|
self.particle_types = list(self.particle_properties.keys())
|
||||||
|
self.current_particle_type = self.particle_types[0] if self.particle_types else 'sand'
|
||||||
self.gravity = 9.8 # m/s^2, adjustable based on the scale of simulation
|
self.gravity = 9.8 # m/s^2, adjustable based on the scale of simulation
|
||||||
self.wind_zones = []
|
self.wind_zones = []
|
||||||
self.wind = [0.0, 0.0] # Global wind vector (x, y)
|
self.wind = [0.0, 0.0] # Global wind vector (x, y)
|
||||||
|
|
||||||
|
|
||||||
def reset_particle_count(self):
|
def reset_particle_count(self):
|
||||||
self.particle_count = 0
|
"""Reset and recalculate accurate particle count"""
|
||||||
|
active_count = np.sum([1 for x, y in self.active_particles if self.particles[x][y] is not None])
|
||||||
|
self.particle_count = int(active_count)
|
||||||
|
|
||||||
|
|
||||||
|
def get_accurate_particle_count(self):
|
||||||
|
"""Get current accurate particle count using numpy"""
|
||||||
|
particle_mask = np.array([[self.particles[x][y] is not None
|
||||||
|
for y in range(self.height)]
|
||||||
|
for x in range(self.width)])
|
||||||
|
return np.sum(particle_mask)
|
||||||
|
|
||||||
|
|
||||||
def get_cell_key(self, x, y): # this is where we get the cell key.
|
def get_cell_key(self, x, y): # this is where we get the cell key.
|
||||||
@ -238,7 +249,9 @@ class Simulation:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Pressure damage
|
# Pressure damage
|
||||||
pressure = self.calculate_forces(particle, x, y)
|
fx, fy = self.calculate_forces(particle, x, y)
|
||||||
|
pressure = (fx*fx + fy*fy)**0.5 # Calculate magnitude of force vector
|
||||||
|
|
||||||
if hasattr(particle, 'pressure_threshold') and pressure > particle.pressure_threshold:
|
if hasattr(particle, 'pressure_threshold') and pressure > particle.pressure_threshold:
|
||||||
particle.durability -= 0.1
|
particle.durability -= 0.1
|
||||||
|
|
||||||
@ -299,7 +312,7 @@ class Simulation:
|
|||||||
|
|
||||||
# Wood to Fire
|
# Wood to Fire
|
||||||
elif target.particle_type == 'wood':
|
elif target.particle_type == 'wood':
|
||||||
if random.random() < 0.3: # 30% chance to ignite
|
if np.random.random() < 0.3: # 30% chance to ignite
|
||||||
self.transform_particle(target_x, target_y, 'fire')
|
self.transform_particle(target_x, target_y, 'fire')
|
||||||
|
|
||||||
|
|
||||||
@ -324,8 +337,8 @@ class Simulation:
|
|||||||
def handle_gas_movement(self, particle, x, y): # this is where we handle the gas movement. this function sucks. wip
|
def handle_gas_movement(self, particle, x, y): # this is where we handle the gas movement. this function sucks. wip
|
||||||
"""Handle gas particle movement"""
|
"""Handle gas particle movement"""
|
||||||
if particle.is_gas:
|
if particle.is_gas:
|
||||||
dx = random.uniform(-1, 1)
|
dx = np.random.uniform(-1, 1)
|
||||||
dy = random.uniform(-2, 0) # Bias upward movement
|
dy = np.random.uniform(-2, 0) # Bias upward movement
|
||||||
new_x = int(x + dx)
|
new_x = int(x + dx)
|
||||||
new_y = int(y + dy)
|
new_y = int(y + dy)
|
||||||
|
|
||||||
@ -349,43 +362,55 @@ class Simulation:
|
|||||||
self.wind_zones.append(wind_zone)
|
self.wind_zones.append(wind_zone)
|
||||||
|
|
||||||
|
|
||||||
def calculate_forces(self, particle, x, y): # this is where we calculate the forces.
|
|
||||||
|
def calculate_forces(self, particle, x, y):
|
||||||
"""Calculate net forces acting on a particle."""
|
"""Calculate net forces acting on a particle."""
|
||||||
fx, fy = 0.0, 0.0 # Initialize forces
|
# Initialize forces as numpy array for vectorized operations
|
||||||
|
forces = np.zeros(2, dtype=np.float32) # [fx, fy]
|
||||||
|
|
||||||
# Check wind zones
|
# Vectorized wind zone calculations
|
||||||
for zone in self.wind_zones:
|
if self.wind_zones:
|
||||||
dx = x - zone['x']
|
positions = np.array([[zone['x'], zone['y']] for zone in self.wind_zones])
|
||||||
dy = y - zone['y']
|
directions = np.array([zone['direction'] for zone in self.wind_zones])
|
||||||
distance = (dx*dx + dy*dy) ** 0.5
|
strengths = np.array([zone['strength'] for zone in self.wind_zones])
|
||||||
if distance <= zone['radius']:
|
radii = np.array([zone['radius'] for zone in self.wind_zones])
|
||||||
fx += zone['direction'][0] * zone['strength'] * (1 - distance/zone['radius'])
|
|
||||||
fy += zone['direction'][1] * zone['strength'] * (1 - distance/zone['radius'])
|
|
||||||
|
|
||||||
# Apply wind force
|
# Calculate distances vectorized
|
||||||
if particle.is_gas:
|
dx = x - positions[:, 0]
|
||||||
fx += self.wind[0] * 0.5
|
dy = y - positions[:, 1]
|
||||||
fy += self.wind[1] * 0.5
|
distances = np.sqrt(dx**2 + dy**2)
|
||||||
else:
|
|
||||||
fx += self.wind[0]
|
|
||||||
fy += self.wind[1]
|
|
||||||
|
|
||||||
# Apply drag force
|
# Apply wind zone forces where distance <= radius
|
||||||
|
mask = distances <= radii
|
||||||
|
scale_factors = np.where(mask, (1 - distances/radii) * strengths[:, np.newaxis], 0)
|
||||||
|
forces += np.sum(directions * scale_factors[:, np.newaxis], axis=0)
|
||||||
|
|
||||||
|
# Apply global wind with vectorized operation
|
||||||
|
wind_factor = 0.5 if particle.is_gas else 1.0
|
||||||
|
forces += np.array(self.wind) * wind_factor
|
||||||
|
|
||||||
|
# Apply drag force vectorized
|
||||||
drag = particle.viscosity * -1
|
drag = particle.viscosity * -1
|
||||||
fx += drag * particle.velocity[0]
|
forces += drag * np.array(particle.velocity)
|
||||||
fy += drag * particle.velocity[1]
|
|
||||||
|
|
||||||
# Check neighboring particles
|
# Neighbor forces using numpy arrays
|
||||||
neighbors = self._get_neighbors_from_grid(x, y)
|
neighbors = np.array(self._get_neighbors_from_grid(x, y))
|
||||||
for nx, ny in neighbors:
|
if len(neighbors):
|
||||||
if(nx, ny) != (x, y):
|
valid_mask = (
|
||||||
if 0 <= nx < self.width and 0 <= ny < self.height:
|
(neighbors[:, 0] >= 0) &
|
||||||
|
(neighbors[:, 0] < self.width) &
|
||||||
|
(neighbors[:, 1] >= 0) &
|
||||||
|
(neighbors[:, 1] < self.height)
|
||||||
|
)
|
||||||
|
neighbors = neighbors[valid_mask]
|
||||||
|
|
||||||
|
for nx, ny in neighbors:
|
||||||
|
if (nx, ny) != (x, y):
|
||||||
neighbor = self.particles[nx][ny]
|
neighbor = self.particles[nx][ny]
|
||||||
if neighbor:
|
if neighbor:
|
||||||
self._apply_neighbor_forces(particle, neighbor, fx, fy)
|
self._apply_neighbor_forces(particle, neighbor, forces[0], forces[1])
|
||||||
|
|
||||||
|
return forces[0], forces[1]
|
||||||
return fx, fy
|
|
||||||
|
|
||||||
|
|
||||||
def _get_quick_neighbors(self, x, y):
|
def _get_quick_neighbors(self, x, y):
|
||||||
@ -426,23 +451,23 @@ class Simulation:
|
|||||||
if neighbor and hasattr(neighbor, 'flamability'):
|
if neighbor and hasattr(neighbor, 'flamability'):
|
||||||
if neighbor.particle_type == 'wood':
|
if neighbor.particle_type == 'wood':
|
||||||
# Higher chance to ignite wood
|
# Higher chance to ignite wood
|
||||||
if random.random() < 0.3: # 30% chance to spread
|
if np.random.random() < 0.3: # 30% chance to spread
|
||||||
self.ignite_particle(neighbor)
|
self.ignite_particle(neighbor)
|
||||||
elif neighbor.flamability > 0:
|
elif neighbor.flamability > 0:
|
||||||
if random.random() < 0.1: # 10% chance for other materials
|
if np.random.random() < 0.1: # 10% chance for other materials
|
||||||
self.ignite_particle(neighbor)
|
self.ignite_particle(neighbor)
|
||||||
|
|
||||||
|
|
||||||
def handle_special_particles(self, particle, x, y): # this is where we handle special particles.
|
def handle_special_particles(self, particle, x, y): # this is where we handle special particles.
|
||||||
"""Handle special particle behaviors"""
|
"""Handle special particle behaviors"""
|
||||||
if particle.particle_type in ['fire', 'flame', 'smoke']:
|
if particle.particle_type in ['fire', 'flame', 'smoke']:
|
||||||
if random.random() < 0.6: # % chance
|
if np.random.random() < 0.6: # % chance
|
||||||
self.particles[x][y] = None
|
self.particles[x][y] = None
|
||||||
self.active_particles.discard((x, y))
|
self.active_particles.discard((x, y))
|
||||||
|
|
||||||
if particle.particle_type in ['fire', 'flame', 'lava']:
|
if particle.particle_type in ['fire', 'flame', 'lava']:
|
||||||
# Create smoke above with proper physics
|
# Create smoke above with proper physics
|
||||||
if random.random() < 0.65 and y > 0: # % chance for smoke
|
if np.random.random() < 0.65 and y > 0: # % chance for smoke
|
||||||
properties = self.particle_properties['smoke']
|
properties = self.particle_properties['smoke']
|
||||||
new_smoke = Particle.from_type((x, y-1), 'smoke', properties)
|
new_smoke = Particle.from_type((x, y-1), 'smoke', properties)
|
||||||
if self.particles[x][y-1] is None:
|
if self.particles[x][y-1] is None:
|
||||||
@ -474,7 +499,7 @@ class Simulation:
|
|||||||
# Transition to gas
|
# Transition to gas
|
||||||
particle.is_gas = True
|
particle.is_gas = True
|
||||||
particle.temperature = 1700
|
particle.temperature = 1700
|
||||||
particle.velocity = [random.uniform(-1, 1), random.uniform(-1, 1)]
|
particle.velocity = [np.random.uniform(-1, 1), np.random.uniform(-1, 1)]
|
||||||
particle.temperature < 1400
|
particle.temperature < 1400
|
||||||
particle.is_gas = False
|
particle.is_gas = False
|
||||||
|
|
||||||
@ -511,7 +536,10 @@ class Simulation:
|
|||||||
grid_x = x // self.particle_size
|
grid_x = x // self.particle_size
|
||||||
grid_y = y // self.particle_size
|
grid_y = y // self.particle_size
|
||||||
|
|
||||||
if 0 <= grid_x < self.width and 0 <= grid_y < self.height:
|
if (0 <= grid_x < self.width and
|
||||||
|
0 <= grid_y < self.height and
|
||||||
|
self.particles[grid_x][grid_y] is None):
|
||||||
|
|
||||||
properties = self.particle_properties[particle_type]
|
properties = self.particle_properties[particle_type]
|
||||||
position = (grid_x, grid_y)
|
position = (grid_x, grid_y)
|
||||||
new_particle = Particle(
|
new_particle = Particle(
|
||||||
@ -521,11 +549,10 @@ class Simulation:
|
|||||||
particle_type=particle_type,
|
particle_type=particle_type,
|
||||||
properties=properties
|
properties=properties
|
||||||
)
|
)
|
||||||
# Add to the grid
|
|
||||||
if 0 <= grid_x < len(self.particles) and 0 <= grid_y < len(self.particles[0]):
|
self.particles[grid_x][grid_y] = new_particle
|
||||||
self.particles[grid_x][grid_y] = new_particle
|
self.active_particles.add((grid_x, grid_y))
|
||||||
self.active_particles.add((grid_x, grid_y))
|
self.particle_count += 1
|
||||||
self.particle_count += 1
|
|
||||||
|
|
||||||
|
|
||||||
def create_particle_circle(self, center_x, center_y): # this is where we create the particle circle.
|
def create_particle_circle(self, center_x, center_y): # this is where we create the particle circle.
|
||||||
@ -544,7 +571,7 @@ class Simulation:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def apply_gravity(self, dt): # this is where we apply gravity.
|
def apply_gravity(self, dt):
|
||||||
"""Handle only gravity and basic particle movement"""
|
"""Handle only gravity and basic particle movement"""
|
||||||
self.spatial_grid.clear()
|
self.spatial_grid.clear()
|
||||||
|
|
||||||
@ -566,15 +593,18 @@ class Simulation:
|
|||||||
if self.particles[x][new_y] is None:
|
if self.particles[x][new_y] is None:
|
||||||
new_x, new_y = x, y + 1
|
new_x, new_y = x, y + 1
|
||||||
else:
|
else:
|
||||||
# Try diagonal movement with randomization
|
# Define diagonal directions first
|
||||||
diagonal_dirs = [(-1, 1), (1, 1)]
|
diagonal_dirs = [(-1, 1), (1, 1)]
|
||||||
random.shuffle(diagonal_dirs)
|
diagonal_dirs = np.array(diagonal_dirs)
|
||||||
|
# Randomize movement directions
|
||||||
|
diagonal_dirs = np.random.permutation(diagonal_dirs)
|
||||||
|
|
||||||
for dx, dy in diagonal_dirs:
|
for dx, dy in diagonal_dirs:
|
||||||
test_x = x + dx
|
test_x = x + dx
|
||||||
test_y = y + dy
|
test_y = y + dy
|
||||||
if (0 <= test_x < self.width and 0 <= test_y < self.height and
|
if (0 <= test_x < self.width and 0 <= test_y < self.height and
|
||||||
self.particles[test_x][test_y] is None):
|
self.particles[test_x][test_y] is None):
|
||||||
if random.random() < 0.8: # 80% chance to move diagonally
|
if np.random.random() < 0.8: # 80% chance to move diagonally
|
||||||
new_x = test_x
|
new_x = test_x
|
||||||
new_y = test_y
|
new_y = test_y
|
||||||
break
|
break
|
||||||
@ -585,8 +615,8 @@ class Simulation:
|
|||||||
new_x = x
|
new_x = x
|
||||||
new_y = y + 1
|
new_y = y + 1
|
||||||
else:
|
else:
|
||||||
spread_directions = [(-1, 0), (1, 0)]
|
spread_directions = np.array([(-1, 0), (1, 0)])
|
||||||
random.shuffle(spread_directions)
|
np.random.shuffle(spread_directions)
|
||||||
for dx, _ in spread_directions:
|
for dx, _ in spread_directions:
|
||||||
test_x = x + dx
|
test_x = x + dx
|
||||||
if (0 <= test_x < self.width and
|
if (0 <= test_x < self.width and
|
||||||
@ -604,8 +634,7 @@ class Simulation:
|
|||||||
particle.position = (new_x, new_y)
|
particle.position = (new_x, new_y)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_physics(self, dt, engine_settings):
|
||||||
def apply_physics(self, dt, engine_settings): # this is where we apply physics.
|
|
||||||
"""Handle all physics effects"""
|
"""Handle all physics effects"""
|
||||||
new_active_particles = set()
|
new_active_particles = set()
|
||||||
updates = []
|
updates = []
|
||||||
@ -615,8 +644,6 @@ class Simulation:
|
|||||||
if not particle:
|
if not particle:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Skip wall physics - walls are immutable
|
# Skip wall physics - walls are immutable
|
||||||
if particle.particle_type == 'wall':
|
if particle.particle_type == 'wall':
|
||||||
new_active_particles.add((x, y))
|
new_active_particles.add((x, y))
|
||||||
@ -628,9 +655,9 @@ class Simulation:
|
|||||||
self.particles[x][y] = None
|
self.particles[x][y] = None
|
||||||
self.active_particles.discard((x, y))
|
self.active_particles.discard((x, y))
|
||||||
|
|
||||||
# Handle fire movement
|
# Handle fire movement using numpy random
|
||||||
dx = random.uniform(-0.5, 0.5)
|
dx = np.random.uniform(-0.5, 0.5)
|
||||||
dy = random.uniform(-1.5, -0.5) # Upward drift
|
dy = np.random.uniform(-1.5, -0.5) # Upward drift
|
||||||
new_x = int(x + dx)
|
new_x = int(x + dx)
|
||||||
new_y = int(y + dy)
|
new_y = int(y + dy)
|
||||||
|
|
||||||
@ -639,12 +666,12 @@ class Simulation:
|
|||||||
self.particles[new_x][new_y] = particle
|
self.particles[new_x][new_y] = particle
|
||||||
new_active_particles.add((new_x, new_y))
|
new_active_particles.add((new_x, new_y))
|
||||||
|
|
||||||
# Generate smoke above the new fire position
|
# Generate smoke above with numpy random
|
||||||
if random.random() < 0.25 and new_y > 0:
|
if np.random.random() < 0.25 and new_y > 0:
|
||||||
properties = self.particle_properties['smoke']
|
properties = self.particle_properties['smoke']
|
||||||
new_smoke = Particle(
|
new_smoke = Particle(
|
||||||
position=(new_x, new_y-1),
|
position=(new_x, new_y-1),
|
||||||
velocity=[random.uniform(-0.5, 0.5), -1],
|
velocity=[np.random.uniform(-0.5, 0.5), -1],
|
||||||
mass=properties.get('mass', 0.1),
|
mass=properties.get('mass', 0.1),
|
||||||
particle_type='smoke',
|
particle_type='smoke',
|
||||||
properties=properties
|
properties=properties
|
||||||
@ -653,13 +680,12 @@ class Simulation:
|
|||||||
self.particles[new_x][new_y-1] = new_smoke
|
self.particles[new_x][new_y-1] = new_smoke
|
||||||
new_active_particles.add((new_x, new_y-1))
|
new_active_particles.add((new_x, new_y-1))
|
||||||
|
|
||||||
# Dissipation chance
|
# Dissipation chance using numpy random
|
||||||
if random.random() < 0.02:
|
if np.random.random() < 0.02:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Air handling - particles can pass through should implement proper air instead of a particle
|
# Air handling
|
||||||
if particle.particle_type == 'air':
|
if particle.particle_type == 'air':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -669,17 +695,17 @@ class Simulation:
|
|||||||
# Calculate forces
|
# Calculate forces
|
||||||
fx, fy = self.calculate_forces(particle, x, y)
|
fx, fy = self.calculate_forces(particle, x, y)
|
||||||
|
|
||||||
# handle gas particles
|
# Handle gas particles
|
||||||
if particle.is_gas:
|
if particle.is_gas:
|
||||||
# Gas-specific movement
|
# Gas movement with numpy random
|
||||||
dx = random.uniform(2, -1)
|
dx = np.random.uniform(2, -1)
|
||||||
dy = random.uniform(-2, 0) # Bias upward
|
dy = np.random.uniform(-2, 0) # Bias upward
|
||||||
new_x = int(x + dx)
|
new_x = int(x + dx)
|
||||||
new_y = int(y + dy)
|
new_y = int(y + dy)
|
||||||
|
|
||||||
self.particles[x][y] = None
|
self.particles[x][y] = None
|
||||||
self.active_particles.discard((x, y))
|
self.active_particles.discard((x, y))
|
||||||
# Check if the new position is within bounds
|
|
||||||
if 0 <= new_x < self.width and 0 <= new_y < self.height:
|
if 0 <= new_x < self.width and 0 <= new_y < self.height:
|
||||||
if self.particles[new_x][new_y] is None:
|
if self.particles[new_x][new_y] is None:
|
||||||
self.particles[x][y] = None
|
self.particles[x][y] = None
|
||||||
@ -694,10 +720,9 @@ class Simulation:
|
|||||||
particle.velocity[1] += (fy / mass) * dt
|
particle.velocity[1] += (fy / mass) * dt
|
||||||
|
|
||||||
if particle.liquid:
|
if particle.liquid:
|
||||||
# Enhanced liquid spreading
|
# Enhanced liquid spreading with numpy random
|
||||||
spread_chance = 0.5
|
if np.random.random() < 0.5:
|
||||||
if random.random() < spread_chance:
|
dx = np.random.choice([-1, 1])
|
||||||
dx = random.choice([-1, 1])
|
|
||||||
if (0 <= x + dx < self.width and
|
if (0 <= x + dx < self.width and
|
||||||
self.particles[x + dx][y] is None):
|
self.particles[x + dx][y] is None):
|
||||||
new_x = x + dx
|
new_x = x + dx
|
||||||
@ -715,31 +740,27 @@ class Simulation:
|
|||||||
if self.particles[new_x][new_y] is None:
|
if self.particles[new_x][new_y] is None:
|
||||||
updates.append((x, y, new_x, new_y, particle))
|
updates.append((x, y, new_x, new_y, particle))
|
||||||
new_active_particles.add((new_x, new_y))
|
new_active_particles.add((new_x, new_y))
|
||||||
# Wake up neighboring dormant particles
|
|
||||||
self._wake_neighbors(new_x, new_y)
|
self._wake_neighbors(new_x, new_y)
|
||||||
else:
|
else:
|
||||||
new_active_particles.add((x, y))
|
new_active_particles.add((x, y))
|
||||||
|
|
||||||
# Apply updates and return new active set
|
# Apply updates
|
||||||
for old_x, old_y, new_x, new_y, particle in updates:
|
for old_x, old_y, new_x, new_y, particle in updates:
|
||||||
self.particles[old_x][old_y] = None
|
self.particles[old_x][old_y] = None
|
||||||
self.particles[new_x][new_y] = particle
|
self.particles[new_x][new_y] = particle
|
||||||
particle.position = (new_x, new_y)
|
particle.position = (new_x, new_y)
|
||||||
|
|
||||||
# Handle boundaries based on settings
|
# Handle boundaries
|
||||||
|
|
||||||
if engine_settings['outerwall']:
|
if engine_settings['outerwall']:
|
||||||
if x <= 0 or x >= self.width-1 or y <= 0 or y >= self.height-1:
|
if x <= 0 or x >= self.width-1 or y <= 0 or y >= self.height-1:
|
||||||
# Create wall particle at boundary if none exists
|
|
||||||
if self.particles[x][y] is None:
|
if self.particles[x][y] is None:
|
||||||
properties = self.particle_properties['wall']
|
properties = self.particle_properties['wall']
|
||||||
wall = Particle.from_type((x, y), 'wall', properties)
|
wall = Particle.from_type((x, y), 'wall', properties)
|
||||||
self.particles[x][y] = wall
|
self.particles[x][y] = wall
|
||||||
new_active_particles.add((x, y))
|
new_active_particles.add((x, y))
|
||||||
self.particle_count += 1 # Track new wall particle
|
self.particle_count += 1
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# Delete particles that go out of bounds
|
|
||||||
if x <= 0 or x >= self.width-1 or y <= 0 or y >= self.height-1:
|
if x <= 0 or x >= self.width-1 or y <= 0 or y >= self.height-1:
|
||||||
if self.particles[x][y] is not None:
|
if self.particles[x][y] is not None:
|
||||||
self.particles[x][y] = None
|
self.particles[x][y] = None
|
||||||
|
|||||||
@ -23,6 +23,7 @@ The `clear_screen` function is used to reset the simulation grid and clear the d
|
|||||||
|
|
||||||
|
|
||||||
from config.settings import pygame, random, particle_properties, engine_settings
|
from config.settings import pygame, random, particle_properties, engine_settings
|
||||||
|
from debug.debugger_system import DebuggerSystem
|
||||||
|
|
||||||
|
|
||||||
class Rendering:
|
class Rendering:
|
||||||
@ -48,6 +49,7 @@ class Rendering:
|
|||||||
'zoom_window': pygame.font.SysFont(None, 20),
|
'zoom_window': pygame.font.SysFont(None, 20),
|
||||||
'zoom_window_text': pygame.font.SysFont(None, 20)
|
'zoom_window_text': pygame.font.SysFont(None, 20)
|
||||||
}
|
}
|
||||||
|
self.debug = DebuggerSystem()
|
||||||
|
|
||||||
# Pre-render static UI elements
|
# Pre-render static UI elements
|
||||||
self.button_surfaces = {}
|
self.button_surfaces = {}
|
||||||
@ -284,52 +286,13 @@ class Rendering:
|
|||||||
|
|
||||||
|
|
||||||
def draw_debug_overlay(self, fps, sim):
|
def draw_debug_overlay(self, fps, sim):
|
||||||
if not engine_settings['enable_fps'] and not engine_settings['enable_debug']:
|
"""Draws debug overlay on the screen."""
|
||||||
return
|
if engine_settings['enable_fps'] or engine_settings['enable_debug']:
|
||||||
|
self.debug.screen = self.screen # Pass screen reference
|
||||||
# Create static debug surface if not exists
|
self.debug.cached_fonts = self.cached_fonts # Pass font references
|
||||||
if not hasattr(self, 'debug_surface'):
|
self.debug.track_fps()
|
||||||
self.debug_surface = pygame.Surface((300, 150), pygame.SRCALPHA)
|
self.debug.update_performance_metrics(sim)
|
||||||
|
self.debug.draw_debug_overlay(self.screen, sim)
|
||||||
# Only update when values change significantly
|
|
||||||
mouse_x, mouse_y = pygame.mouse.get_pos()
|
|
||||||
grid_x, grid_y = mouse_x // 3, mouse_y // 3
|
|
||||||
|
|
||||||
current_info = (
|
|
||||||
int(fps),
|
|
||||||
int(sim.track_tps()),
|
|
||||||
mouse_x // 10, # Reduced update frequency
|
|
||||||
mouse_y // 10,
|
|
||||||
sim.particle_count
|
|
||||||
)
|
|
||||||
|
|
||||||
if not hasattr(self, '_last_debug_info') or current_info != self._last_debug_info:
|
|
||||||
self._last_debug_info = current_info
|
|
||||||
self.debug_surface.fill((0, 0, 0, 0))
|
|
||||||
|
|
||||||
font = self.cached_fonts['debug']
|
|
||||||
y_offset = 10
|
|
||||||
|
|
||||||
if engine_settings['enable_fps']:
|
|
||||||
fps_surf = font.render(f"FPS: {fps:.1f} | TPS: {sim.track_tps():.1f}", True, (255, 255, 255))
|
|
||||||
self.debug_surface.blit(fps_surf, (10, y_offset))
|
|
||||||
y_offset += 25
|
|
||||||
|
|
||||||
if engine_settings['enable_debug']:
|
|
||||||
debug_lines = [
|
|
||||||
f"Mouse: ({mouse_x}, {mouse_y})",
|
|
||||||
f"Grid: ({grid_x}, {grid_y})",
|
|
||||||
f"Particles: {sim.particle_count}"
|
|
||||||
]
|
|
||||||
|
|
||||||
for line in debug_lines:
|
|
||||||
text_surf = font.render(line, True, (255, 255, 255))
|
|
||||||
self.debug_surface.blit(text_surf, (10, y_offset))
|
|
||||||
y_offset += 25
|
|
||||||
|
|
||||||
# Single blit of cached surface
|
|
||||||
self.screen.blit(self.debug_surface, (0, 0))
|
|
||||||
|
|
||||||
|
|
||||||
def draw_buttons(self): # this is the function that draws the buttons
|
def draw_buttons(self): # this is the function that draws the buttons
|
||||||
self.buttons = {}
|
self.buttons = {}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ The main loop runs at a target frame rate of 60 FPS (this fps varies on my mood
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Import Require files for the Engine.
|
# Import Require files for the Engine.
|
||||||
from config.settings import pygame, engine_settings, cProfile, pstats
|
from config.settings import pygame, engine_settings, cProfile, pstats, time
|
||||||
|
|
||||||
from rendering.rendering import Rendering
|
from rendering.rendering import Rendering
|
||||||
from physics.sim import Simulation
|
from physics.sim import Simulation
|
||||||
@ -274,9 +274,14 @@ def main():
|
|||||||
if mouse_down_left and not over_button:
|
if mouse_down_left and not over_button:
|
||||||
x, y = mouse_pos
|
x, y = mouse_pos
|
||||||
if sim.current_particle_type not in ['wind', 'air']:
|
if sim.current_particle_type not in ['wind', 'air']:
|
||||||
|
current_time = time.time()
|
||||||
|
if not hasattr(main, 'last_particle_time'):
|
||||||
|
main.last_particle_time = 0
|
||||||
|
|
||||||
|
# Limit particle creation to every 16ms (approximately 60 FPS)
|
||||||
|
if current_time - main.last_particle_time >= 0.016:
|
||||||
sim.create_particle_circle(x, y)
|
sim.create_particle_circle(x, y)
|
||||||
else:
|
main.last_particle_time = current_time
|
||||||
sim.add_wind_zone(x, y)
|
|
||||||
|
|
||||||
if mouse_down_right:
|
if mouse_down_right:
|
||||||
x, y = mouse_pos
|
x, y = mouse_pos
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user