Skip to content

Instantly share code, notes, and snippets.

@rahulbhadani
Created January 31, 2023 06:16
Show Gist options
  • Save rahulbhadani/bd2529f9897145b1e5ac5e5908329ecd to your computer and use it in GitHub Desktop.
Save rahulbhadani/bd2529f9897145b1e5ac5e5908329ecd to your computer and use it in GitHub Desktop.
Canvas with grids in Pygame
import pygame
from pygame import gfxdraw
import numpy as np
class Window:
def __init__(self, sim=None, config={}):
# Simulation to draw
#self.sim = sim
# Set default configurations
self.set_default_config()
# Update configurations
for attr, val in config.items():
setattr(self, attr, val)
def set_default_config(self):
"""Set default configuration"""
self.width = 1400
self.height = 1000
self.bg_color = (250, 250, 250)
self.fps = 60
self.zoom = 5
self.offset = (0, 0)
self.mouse_last = (0, 0)
self.mouse_down = False
def loop(self, loop=None):
"""Shows a window visualizing the simulation and runs the loop function."""
# Create a pygame window
self.screen = pygame.display.set_mode((self.width, self.height))
pygame.display.flip()
# Fixed fps
clock = pygame.time.Clock()
# To draw text
pygame.font.init()
self.text_font = pygame.font.SysFont('Lucida Console', 16)
# Draw loop
running = True
while running:
# Update simulation
#if loop: loop(self.sim)
# Draw simulation
self.draw()
# Update window
pygame.display.update()
clock.tick(self.fps)
# Handle all events
for event in pygame.event.get():
# Quit program if window is closed
if event.type == pygame.QUIT:
running = False
# Handle mouse events
elif event.type == pygame.MOUSEBUTTONDOWN:
# If mouse button down
if event.button == 1:
# Left click
x, y = pygame.mouse.get_pos()
x0, y0 = self.offset
self.mouse_last = (x-x0*self.zoom, y-y0*self.zoom)
self.mouse_down = True
if event.button == 4:
# Mouse wheel up
self.zoom *= (self.zoom**2+self.zoom/4+1) / (self.zoom**2+1)
if event.button == 5:
# Mouse wheel down
self.zoom *= (self.zoom**2+1) / (self.zoom**2+self.zoom/4+1)
elif event.type == pygame.MOUSEMOTION:
# Drag content
if self.mouse_down:
x1, y1 = self.mouse_last
x2, y2 = pygame.mouse.get_pos()
self.offset = ((x2-x1)/self.zoom, (y2-y1)/self.zoom)
elif event.type == pygame.MOUSEBUTTONUP:
self.mouse_down = False
def convert(self, x, y=None):
"""Converts simulation coordinates to screen coordinates"""
if isinstance(x, list):
return [self.convert(e[0], e[1]) for e in x]
if isinstance(x, tuple):
return self.convert(*x)
return (
int(self.width/2 + (x + self.offset[0])*self.zoom),
int(self.height/2 + (y + self.offset[1])*self.zoom)
)
def inverse_convert(self, x, y=None):
"""Converts screen coordinates to simulation coordinates"""
if isinstance(x, list):
return [self.convert(e[0], e[1]) for e in x]
if isinstance(x, tuple):
return self.convert(*x)
return (
int(-self.offset[0] + (x - self.width/2)/self.zoom),
int(-self.offset[1] + (y - self.height/2)/self.zoom)
)
def background(self, r, g, b):
"""Fills screen with one color."""
self.screen.fill((r, g, b))
def line(self, start_pos, end_pos, color):
"""Draws a line."""
gfxdraw.line( self.screen, *start_pos, *end_pos, color)
def draw_axes(self, color=(100, 100, 100)):
"""Draw x and y axis"""
x_start, y_start = self.inverse_convert(0, 0)
x_end, y_end = self.inverse_convert(self.width, self.height)
self.line( self.convert((0, y_start)), self.convert((0, y_end)), color )
self.line( self.convert((x_start, 0)), self.convert((x_end, 0)), color )
def draw_grid(self, unit=50, color=(150,150,150)):
"""Draws a grid"""
x_start, y_start = self.inverse_convert(0, 0)
x_end, y_end = self.inverse_convert(self.width, self.height)
n_x = int(x_start / unit)
n_y = int(y_start / unit)
m_x = int(x_end / unit)+1
m_y = int(y_end / unit)+1
for i in range(n_x, m_x):
self.line(self.convert((unit*i, y_start)), self.convert((unit*i, y_end)), color )
for i in range(n_y, m_y):
self.line(self.convert((x_start, unit*i)), self.convert((x_end, unit*i)), color )
def draw_roads(self):
"""Draws every road"""
pass
def draw_status(self):
"""Draws status text"""
text_fps = self.text_font.render(f't={20.0:.5}', False, (0, 0, 0))
text_frc = self.text_font.render(f'n={1000}', False, (0, 0, 0))
self.screen.blit(text_fps, (0, 0))
self.screen.blit(text_frc, (100, 0))
def draw(self):
# Fill background
self.background(*self.bg_color)
# Major and minor grid and axes
self.draw_grid(10, (220,220,220))
self.draw_grid(100, (200,200,200))
self.draw_axes()
# Draw status info
self.draw_status()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment