175 lines
7.2 KiB
Python
175 lines
7.2 KiB
Python
import pygame
|
|
import random
|
|
import math
|
|
|
|
# Initialize pygame
|
|
pygame.init()
|
|
|
|
# Screen dimensions and panel sizes
|
|
WIDTH, HEIGHT = 800, 600
|
|
PANEL_HEIGHT = 150 # Bottom panel for sliders
|
|
SIM_HEIGHT = HEIGHT - PANEL_HEIGHT # Simulation area height
|
|
|
|
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
|
pygame.display.set_caption("Particle Simulation with Adjustable Variables")
|
|
clock = pygame.time.Clock()
|
|
|
|
# Define a simple Slider class
|
|
class Slider:
|
|
def __init__(self, x, y, w, h, min_val, max_val, init_val, label):
|
|
self.x = x
|
|
self.y = y
|
|
self.w = w
|
|
self.h = h
|
|
self.min_val = min_val
|
|
self.max_val = max_val
|
|
self.value = init_val
|
|
self.label = label
|
|
self.dragging = False
|
|
|
|
def handle_event(self, event):
|
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
if event.button == 1:
|
|
if self.get_knob_rect().collidepoint(event.pos):
|
|
self.dragging = True
|
|
elif event.type == pygame.MOUSEBUTTONUP:
|
|
if event.button == 1:
|
|
self.dragging = False
|
|
elif event.type == pygame.MOUSEMOTION:
|
|
if self.dragging:
|
|
mx = event.pos[0]
|
|
# Clamp mx inside slider range
|
|
relative = (mx - self.x) / self.w
|
|
relative = max(0, min(1, relative))
|
|
self.value = self.min_val + relative * (self.max_val - self.min_val)
|
|
|
|
def get_knob_rect(self):
|
|
# Determine knob position along slider track
|
|
relative = (self.value - self.min_val) / (self.max_val - self.min_val)
|
|
knob_x = self.x + relative * self.w
|
|
knob_width = 10
|
|
knob_height = self.h
|
|
return pygame.Rect(knob_x - knob_width // 2, self.y, knob_width, knob_height)
|
|
|
|
def draw(self, surface, font):
|
|
# Draw slider track (a thin rectangle)
|
|
track_rect = pygame.Rect(self.x, self.y + self.h // 2 - 2, self.w, 4)
|
|
pygame.draw.rect(surface, (200, 200, 200), track_rect)
|
|
# Draw knob
|
|
knob_rect = self.get_knob_rect()
|
|
pygame.draw.rect(surface, (100, 100, 100), knob_rect)
|
|
# Draw label and current value (as plain text)
|
|
label_surface = font.render(f"{self.label}: {self.value:.2f}", True, (0, 0, 0))
|
|
surface.blit(label_surface, (self.x, self.y - 20))
|
|
|
|
# Particle class
|
|
class Particle:
|
|
def __init__(self, pos, velocity, lifetime, size, color=(255, 255, 255)):
|
|
self.pos = pygame.math.Vector2(pos)
|
|
self.vel = pygame.math.Vector2(velocity)
|
|
self.lifetime = lifetime
|
|
self.size = size
|
|
self.initial_lifetime = lifetime
|
|
self.color = color
|
|
|
|
def update(self, gravity):
|
|
self.vel.y += gravity
|
|
self.pos += self.vel
|
|
self.lifetime -= 1
|
|
|
|
def is_dead(self):
|
|
return self.lifetime <= 0
|
|
|
|
def draw(self, surface, glow_intensity):
|
|
# Draw glow effect if glow_intensity is greater than zero
|
|
if glow_intensity > 0:
|
|
# Create a temporary surface for glow with per-pixel alpha
|
|
glow_surface = pygame.Surface((self.size*6, self.size*6), pygame.SRCALPHA)
|
|
glow_center = self.size * 3
|
|
# Draw several concentric circles for the glow effect
|
|
for i in range(1, 6):
|
|
# Alpha decreases as the circle radius increases
|
|
alpha = max(0, int(255 * (glow_intensity / 10) * (1 - i/6)))
|
|
radius = int(self.size + i * 2)
|
|
pygame.draw.circle(glow_surface,
|
|
(self.color[0], self.color[1], self.color[2], alpha),
|
|
(glow_center, glow_center),
|
|
radius)
|
|
# Blit the glow surface with additive blending
|
|
surface.blit(glow_surface, (self.pos.x - self.size*3, self.pos.y - self.size*3), special_flags=pygame.BLEND_RGBA_ADD)
|
|
# Draw the particle itself
|
|
pygame.draw.circle(surface, self.color, (int(self.pos.x), int(self.pos.y)), int(self.size))
|
|
|
|
# Create sliders for each variable, arranged in two rows within the bottom panel.
|
|
font = pygame.font.SysFont("Arial", 16)
|
|
sliders = []
|
|
# Row 1: Emission Rate, Speed, Lifetime
|
|
sliders.append(Slider(x=20, y=SIM_HEIGHT + 10, w=200, h=20, min_val=0, max_val=50, init_val=5, label="Emission Rate"))
|
|
sliders.append(Slider(x=240, y=SIM_HEIGHT + 10, w=200, h=20, min_val=0, max_val=10, init_val=5, label="Speed"))
|
|
sliders.append(Slider(x=460, y=SIM_HEIGHT + 10, w=200, h=20, min_val=10, max_val=300, init_val=100, label="Lifetime"))
|
|
# Row 2: Size, Gravity, Glow
|
|
sliders.append(Slider(x=20, y=SIM_HEIGHT + 50, w=200, h=20, min_val=1, max_val=20, init_val=5, label="Size"))
|
|
sliders.append(Slider(x=240, y=SIM_HEIGHT + 50, w=200, h=20, min_val=0, max_val=2, init_val=0.1, label="Gravity"))
|
|
sliders.append(Slider(x=460, y=SIM_HEIGHT + 50, w=200, h=20, min_val=0, max_val=10, init_val=3, label="Glow"))
|
|
|
|
# List to hold all particles
|
|
particles = []
|
|
|
|
# Emitter position (center of simulation area)
|
|
emitter = pygame.math.Vector2(WIDTH // 2, SIM_HEIGHT // 2)
|
|
|
|
# Main loop
|
|
running = True
|
|
while running:
|
|
# Process events (for quitting and slider events)
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
running = False
|
|
for slider in sliders:
|
|
slider.handle_event(event)
|
|
|
|
# Get simulation variables from slider values
|
|
emission_rate = sliders[0].value # particles per frame
|
|
speed = sliders[1].value # initial speed magnitude
|
|
lifetime = sliders[2].value # lifetime in frames
|
|
size = sliders[3].value # particle size (radius)
|
|
gravity = sliders[4].value # gravity acceleration
|
|
glow = sliders[5].value # glow intensity
|
|
|
|
# Spawn new particles (round emission_rate to an integer count)
|
|
for _ in range(int(emission_rate)):
|
|
# Spawn at the emitter with a random direction
|
|
angle = random.uniform(0, 2 * math.pi)
|
|
velocity = pygame.math.Vector2(math.cos(angle), math.sin(angle)) * speed
|
|
particles.append(Particle(emitter, velocity, lifetime, size))
|
|
|
|
# Update particles and remove dead ones
|
|
for particle in particles[:]:
|
|
particle.update(gravity)
|
|
if particle.is_dead():
|
|
particles.remove(particle)
|
|
|
|
# Clear simulation area (fill with black)
|
|
screen.fill((0, 0, 0), rect=pygame.Rect(0, 0, WIDTH, SIM_HEIGHT))
|
|
# Optionally fill the control area with a light color (or leave it as is)
|
|
screen.fill((220, 220, 220), rect=pygame.Rect(0, SIM_HEIGHT, WIDTH, PANEL_HEIGHT))
|
|
|
|
# Draw all particles
|
|
for particle in particles:
|
|
particle.draw(screen, glow_intensity=glow)
|
|
|
|
# Draw slider controls in the bottom panel
|
|
for slider in sliders:
|
|
slider.draw(screen, font)
|
|
|
|
# Draw debug text (plain text, no background) at the top-left corner of the simulation area
|
|
debug_text = (f"Particles: {len(particles)} | Emission Rate: {emission_rate:.2f} | Speed: {speed:.2f} | "
|
|
f"Lifetime: {lifetime:.2f} | Size: {size:.2f} | Gravity: {gravity:.2f} | Glow: {glow:.2f}")
|
|
debug_surface = font.render(debug_text, True, (255, 255, 255))
|
|
screen.blit(debug_surface, (10, 10))
|
|
|
|
pygame.display.flip()
|
|
clock.tick(60)
|
|
|
|
pygame.quit()
|