Reputation: 8678
I am trying to build an application that requires the user to draw something.
To do so, I create a canvas (a pygame.Surface object) on which the drawing are registered, and then I blit it onto the window. I'd like the canvas to be infinite, so that when the user scrolls he can continue drawing (of course only a small part of the canvas is blited onto the window). But, actually, Surface in pygame requires a finite width and height and, most importantly, it's not that big! (I think that's because it actually locks the space in memory).
So, I tried to create chunks: every chunk has given fixed size (like, twice the screen size), and to each chunk is allocated a certain Surface. Chunks are created dynamically, on demand, and it overall works pretty well.
My problem is that when I try to draw lines that cross onto multiple chunks, it requires a great effort to compute onto which chunks that line should actually be drawn, and in what pieces it should be broken. I didn't even try to draw rectangles because it really was a pain to make the 'draw-a-line' function work.
That's when I thought that what I was doing was fundamentally wrong: instead of trying to rewrite all of pygame.draw and pygame.gfxdraw functions so that they basically do a per-chunk work, I should really overload the pygame.Surface (say, create a MySurface class child of Surface) so whenever a pixel is modified, I internally chose to which chunk it belongs and actually change it on that chunk, and pass that new Surface object to the pygame functions.
I've searched a lot at the pygame doc, but there it isn't explained how to do that. I don't even know what methods of a Surface object are internally called when I blit/draw onto it! I also google it and I didn't find anyone trying to do that kind of stuff (maybe I'm going the wrong way?).
So, my question(s) is: is this the right approach? And, if yes, how should I realize it?
I don't post code because what I need is more an explanation on where to find the doc of what I try to do more than a code review.
Upvotes: 3
Views: 566
Reputation: 101072
You can't just subclass Surface
, because it's not written in python, but in C. Here's the source code; look for yourself.
You could take another approach and instead of calculating where to draw stuff, blit it onto a temporary Surface
first and blit that to the chunks relative to the chunk's position.
Here's simple example I hacked together:
import pygame
class Chunk(pygame.sprite.Sprite):
def __init__(self, grid_pos, size, color):
super().__init__()
self.image = pygame.Surface(size)
self.rect = self.image.get_rect(
x = grid_pos[0] * size[0],
y = grid_pos[1] * size[1]
)
self.image.fill(pygame.Color(color))
def patch(self, surface):
self.image.blit(surface, (-self.rect.x, -self.rect.y))
def main():
pygame.init()
size = 800, 600
screen = pygame.display.set_mode(size)
chunks = pygame.sprite.Group(
Chunk((0,0), size, 'green'),
Chunk((1,0), size, 'red'),
Chunk((0,1), size, 'blue'),
Chunk((1,1), size, 'yellow')
)
dragging = None
drawing = None
tmp_s = pygame.Surface(size, pygame.SRCALPHA)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 3:
dragging = event.pos
if event.button == 1:
drawing = event.pos
if event.type == pygame.MOUSEBUTTONUP:
if event.button == 3:
dragging = None
if event.button == 1:
drawing = None
for chunk in chunks:
chunk.patch(tmp_s)
if event.type == pygame.MOUSEMOTION:
if dragging:
for chunk in chunks:
chunk.rect.move_ip(event.rel)
screen.fill((0, 0, 0))
chunks.draw(screen)
tmp_s.fill((0,0,0,0))
if drawing:
size = pygame.Vector2(pygame.mouse.get_pos()) - drawing
pygame.draw.rect(tmp_s, pygame.Color('white'), (*drawing, *size), 10)
screen.blit(tmp_s, (0, 0))
chunks.update()
pygame.display.flip()
main()
As you can see, the canvas consists of 4 chunks. Use the right mouse button to move the canvas and the left button to start drawing a rect.
Upvotes: 4